Dart math calculations are inaccurate [duplicate] - dart

This question already has answers here:
Is floating point math broken?
(31 answers)
How do you round a double in Dart to a given degree of precision AFTER the decimal point?
(28 answers)
Closed 12 months ago.
Dart calculations are off. We're running into rounding issues (financial app)
Here an example
Dartpad output
Hours : 7.5
Rate : 19.61
Val (Hours * Rate) : 147.075
Val * 100 (should be 14707.5) : 14707.499999999998
Round Val (now rounds down) : 14707
Val / 100 : 147.07 (should have been 147.08)
This is causing rounding errors displaying values to 2 decimal places.
Is there a way to accomplish this accurately in dart?

This is a floating point precision thing, nothing really to do with the language itself. There are many ways around this problem. See mine below.
void main() {
var vHours = 7.5;
print('Hours : $vHours');
var vRate = 19.61;
print('Rate : $vRate');
var vValue = vHours * vRate;
print('Val (Hours * Rate) : $vValue');
print('');
var vMul = (vValue * 100);
var vMulString = vMul.toStringAsFixed(2);
var vMulParsed = num.parse(vMulString);
print('Val * 100 (should be 14707.5) : $vMulParsed');
var vMulRounded = vMulParsed.round();
print('Round Val (now rounds down) : $vMulRounded');
var vDiv = vMulRounded / 100;
print('Val / 100 : $vDiv (should have been 147.08)');
}

Related

Convert a continuous range of doubles to a discrete int range

i want to map a continuous range of double values [10.0,20.0] to a byte in the range of [100,200];
The following must apply (even when converting back and forth multiple times):
convertToByte(convertToDouble(y)) == y
Thanks.
With help of the comments to the question we're using the following code:
int convertToByte(
double initial,
double minValue,
double maxValue,
int minByte,
int maxByte,
) {
double value = initial - minValue;
double valueRange = maxValue - minValue;
int byteRange = maxByte - minByte;
double valueSteps = valueRange / byteRange;
double byte = (value / valueSteps);
return (minByte + byte.round()).clamp(minByte, maxByte);
}
This does not provide a solution for the specified test, but a deterministic answer for a specific value.
When converting a byte back to a value and vice versa multiple times the output always stays the same.
This is what we needed for our application.

Truncate to 2 decimal places without rounding

Can't find or work out a solution to this that works with Dart.
I've already tried:
1. toStringAsFixed() - this rounds
2. double.parse(number.toStringAsFixed()) - this rounds
3. num - num % 0.01 - this rounds
I've found some solutions on SO but they either use functions that aren't available on Flutter/Dart or didn't work for me.
Any help would be greatly appreciated.
To build on #Irn beautiful Answer.
The function below lets you specify how many fractional digits / how many decimal places you want
double truncateToDecimalPlaces(num value, int fractionalDigits) => (value * pow(10,
fractionalDigits)).truncate() / pow(10, fractionalDigits);
Example
truncateToDecimalPlace(4321.92385678, 3) // 4321.923
Or my favorite way is to use Dart extensions
extension TruncateDoubles on double {
double truncateToDecimalPlaces(int fractionalDigits) => (this * pow(10,
fractionalDigits)).truncate() / pow(10, fractionalDigits);
}
This lets you call the function on a double object like thus
Usage
4321.92385678.truncateToDecimalPlaces(3); // 4321.923
Try
double truncateToHundreths(num value) => (value * 100).truncate() / 100;
There will be cases where you lose precision, but that requires you to use almost all 53 bits of double precision in the number (when multiplying by 100 loses precision to begin with).
Going trough strings is a significant overhead that I would avoid if possible, unless you actually expect to convert the number to a string anyway afterwards.
(value * 100).truncateToDouble() / 100
Example:
var lat = 29.4562
lat * 100 = 2945.62
2945.62 truncateToDouble() = 2945
2945 / 100 = 29.45
I have used the below method
String toFixed(double value, [int decimalPlace = 1]) {
try {
String originalNumber = value.toString();
List<String> formattedNumber = originalNumber.split('.');
return "${formattedNumber[0]}.${formattedNumber[1].substring(0, decimalPlace)}";
} catch (_) {}
return value.toString();
}

Difference between x = -5.abs() and print(x.abs())? [duplicate]

This question already has an answer here:
why abs() function in dart return negative number when not wrapped in parenthesis?
(1 answer)
Closed 12 months ago.
void main() {
var x = -5.abs();
print(x);
}
Will be -5.
But:
void main() {
var x = -5;
print(x.abs());
}
Will be 5.
-5 is an object, and the result of -5.abs() will be connected to x. But we still see -5. Could you please correct me where I wrong?
I think what Dart does is:
var x = (-1)*5.abs();
You can use var x = (-5).abs(); to get 5
void main() {
var x = -5.abs();
print(x);
}
is almost certainly parsed as
void main() {
var x = -(5.abs());
print(x);
}
and since 5 is already positive, it comes back unchanged. And then you negate that result.

Convert high numbers to lower format [duplicate]

This question already has answers here:
NSNumberFormatter : Show 'k' instead of ',000' in large numbers?
(3 answers)
How to get file size properly and convert it to MB, GB in Cocoa? [duplicate]
(3 answers)
Closed 6 years ago.
How can I change high numbers to something like:
1,000 = 1K
1,250 = 1,2K
10,200 = 10,2K
102,000 = 102K
1,200,000 = 1,2M
Or something like that?
This is how I set the number:
textCell?.ll1.text = "\(String(thumb[indexPath.row].count))"
textCell?.ll2.text = "\(String(love[indexPath.row].count))"
let formatter = NSByteCountFormatter()
That's it ;)
Examples:
let oneFormattedNumber = formatter.stringFromByteCount(1025000000000)
let formattedList = [1_000, 1_250, 10_200, 102_000, 1_200_000].map(formatter.stringFromByteCount)
You can add this functionality as an extension to Int:
extension Int {
func shortLiteralDescription() -> String {
var factor = 0
let tokens = ["","K", "M", "G","T","P"] //If you think you will need to express more, add them here
var value = Double(self);
while (value > 1000) {
value /= 1000
factor++
}
return "\(value)\(tokens[factor])"
}
}
And then:
400200.shortLiteralDescription() //400.2K
4000.shortLiteralDescription() //4.0K

Swift: String from float without rounding the values

It is recommended to round the decimals but i am facing an scenario where i just need to cut down the precision.
Output: 15.96 to 16.0
Desired output: 15.96 to 15.9
Codes:
var value: AnyObject = dict.valueForKey("XXX")!
var stringVal = NSString(format:"%.1f", value.floatValue)
I thought this will be simple but found tricky. Your thoughts on this is highly appreciated.
Use a NSNumberFormatter and configure its rounding mode accordingly:
let formatter = NSNumberFormatter()
formatter.maximumFractionDigits = 1
formatter.roundingMode = .RoundDown
let s = formatter.stringFromNumber(15.96)
// Result: s = "15.9"
If you need to use the rounded number in future math operations, you can use the following function:
func roundToPlaces(_ value: Double, places: Int, rule: FloatingPointRoundingRule = .toNearestOrAwayFromZero) -> Double {
let divisor = pow(10.0, Double(places))
return (value * divisor).rounded(rule) / divisor
}
You can then call it with
var value: AnyObject = dict.valueForKey("XXX")!
var rounded = roundToPlaces(value.doubleValue, places: 1, rule: .down)
var stringVal = "\(rounded)"
What this actually did was the following:
15.96 * 10.0 = 159.6
floor(159.6) = 159.0
159.0 / 10.0 = 15.9
Caveat: This won't help in situations where you're using scientific precision, i.e.
1.49850e0 --> 1.4e0 // (5 places --> 1 place)
1.39e10 --> 1.3e10 // (3 places --> 1 place)
It will treat all numbers as e0
[update 2018-08-09]
Since it seems like my answer is getting some views, I would like to point out that rounding floating-point numbers by division can introduce errors because of how floating-point numbers are stored in memory. as user #mattt has pointed out elsewhere:
floor(1.5679999 * 1000) / 1000 == 1.5669999999999999
(if you want to get your math on, this paper is a great primer on numbers and computers)
If you need that level of precision, use fixed-point numbers instead. Swift provides the Decimal type for this purpose.
The important thing is to understand your problem. If you're working with money or sensor data, you probably want Decimals. If you're working with computer graphics, you can go with Floats.
Try using this:
var test : AnyObject = "15.96"
var rounded_down = floorf(test.floatValue * 10) / 10;
print(rounded_down)
Here's an updated answer in Swift 5 based on #Clafou 's answer. You can use it as an extension to any data type. Example
extension Double {
func cutToDecimalPlace(_ decimalPlaces: Int) -> String{
let formatter = NumberFormatter()
formatter.maximumFractionDigits = decimalPlaces
formatter.roundingMode = .down
return formatter.string(from: NSNumber(value: self)) ?? ""
}
}
And you can call it like this
let priceValueString = "24.124"
let updatedPriceValue = priceValueString.doubleValue.cutToDecimalPlace(1)
Output will be
24.1

Resources