Closed asier-escofet-sagarribay closed 1 day ago
Same question with more precision.
// Got 1.123 expected 1.124 NumberFormat("#.###").format(1.1235)
Okay, but this really looks strange.
In the following example, I make the pattern one character shorter than the input each time to test the round function, and the last digit is sometimes 6 and sometimes 5.
print('# ${NumberFormat("#").format(5.5)}');
print('#.# ${NumberFormat("#.#").format(5.55)}');
print('#.## ${NumberFormat("#.##").format(5.555)}');
print('#.### ${NumberFormat("#.###").format(5.5555)}');
print('#.#### ${NumberFormat("#.####").format(5.55555)}');
print('#.##### ${NumberFormat("#.#####").format(5.555555)}');
print('#.# ${NumberFormat("#.#").format(5.550001)}');
Output:
# 6
#.# 5.5
#.## 5.55
#.### 5.556
#.#### 5.5556
#.##### 5.55556
#.# 5.6
In my understanding, regardless of whether mathematical rounding or banker's rounding(IEEE 754) is used, the result should be the same, either all 5s or all 6s.
@FourLeafTec, the results are surprising to you, presumably, because you are assuming that a piece of source code like 5.55
is evaluated at run time as a floating point representation whose value is precisely 5.55. This is not true. For example:
void main() {
print(5.5.toStringAsFixed(20)); // 5.50000000000000000000
print(5.55.toStringAsFixed(20)); // 5.54999999999999982236
print(5.555.toStringAsFixed(20)); // 5.55499999999999971578
print(5.5555.toStringAsFixed(20)); // 5.55550000000000032685
print(5.55555.toStringAsFixed(20)); // 5.55555000000000021032
print(5.555555.toStringAsFixed(20)); // 5.55555500000000002103
print(5.5555555.toStringAsFixed(20)); // 5.55555549999999964683
print(5.55555555.toStringAsFixed(20)); // 5.55555555000000023114
}
The reason why the floating point value (using a representation that is specified in IEEE 754) cannot precisely represent a value like 5.55 is that it uses the binary rather than the decimal number system. There is simply no finite sequence of binary digits that yields the value 5.55. In contrast, 5.5 decimal is exactly 101.1 binary.
It's somewhat similar to the situation with the decimal number system and a value like a third: values like 0.3, 0.33, 0.333 are better and better approximations of one third, but there is no finite sequence of digits that encodes one third exactly.
Using the number system with base 3 it would be trivial: 0.1 (as a "ternary" number) is exactly one third.
When you don't start out with precisely 5.55 but rather have 5.54999999999999982236 (approximately, because we are now looking at a decimal encoding of a value which was given in binary ;-), rounding will behave differently.
So it is still working as specified. ;-)
OMG!
I can't believe I was so foolish to forget about the precision loss issue with float/double. @mosuem You're absolutely right, this is working as intended. @eernstg Thank you so much for clarifying this for me.
Bug When the last decimal to round is a 5:
To Reproduce
System info Dart SDK 3.3.0 Flutter SDK 3.19.0 intl 0.19.0