9999999999999999.0 – 9999999999999998.0
geocar.sdf1.org
geocar.sdf1.org
I'm somewhat baffled by this statement. If a Go program compared a constant expression float against a runtime computed float, it could have unexpected results, but comparing floats in general is dangerous. I don't see how this language quirk increases that danger in a meaningful way.
That said, for the negatives it comes with it does come with positives as well and I think that makes it worth it.
It's another thing if a language sometimes outputs two and sometimes outputs one depending on the syntax of the request. This is reasonable where that syntax change is not an explicit cast to double precision or single precision, but dangerous if it uses a behind-the-scenes default of arbitrary precision in compiled literals and a default of FP32 in implicitly typed literal assignments.
var d = -0.0
is different than var d = 0.0
d = -d
This IMO crosses the line into "outright bug" territory. >>> 9007199254740993.0
9007199254740992.0
What's really surprising is that this number is only ~16 million in single precision floats.Integers have absolute precision, at the expense of either range (say, i64) or arbitrarily growing size.
What does this mean? Surely even single precision floating point can represent a number far closer to the original than 16 million? Edit: It appears the closest number in single precision is 9007199000000000.0
Edit2: Oh I see: you mean that the first number that cannot be represented precisely by a single-precision float is ~16 million.
Half of all floats are in [-1, 1].
$ bc -l
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software
Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
9999999999999999.0 - 9999999999999998.0
1.0It also gives the math result here of course.
% dc
9999999999999999.0 9999999999999998.0 - p
1.0
%I used bc's compiled output ("bc -c") to learn how to make dc jump through hoops.
function calc2 { if [ $# -eq 0 ]; then echo 'pass commands for math evaluation, like calc l(2^32/17) + 3'; fi; echo $* | bc -l; }
= () { printf "%'.f\n" $(echo $1 | bc); }; alias calc==
>>> 9999999999999999.0 == 10000000000000000.0
True
>>> from decimal import Decimal as D
>>> D(9999999999999999.0)
Decimal('10000000000000000')
"It's a simple question" I find it very much not simple question; indeed I'm not sure what is the expected "right answer" here?Definitely keeping this example in my back pocket for explaining to people why floating point is not what you think it is.
I take issue with this. Drift from floating point inaccuracies can compound quickly and dramatically affect results. Sure if you're just looping over a 1000 item list, it's not going to matter that JavaScript is representing that as a float/double, but in a wide variety of contexts, such as anything to do with money, it absolutely does matter.
apparently it's also because of the "what is up?" question.
e.g. in outer wilds ... how do you determine which way is "up" when "up" for the player can be any direction.
BTW this is one of the reasons why you should never represent money as a float, except when making a rough estimate. Another, bigger reason is that 0.1 is an infinite repeating fraction in binary, so it can't be represented exactly.
I ran into a site that broke because they were using 64b unix nanotime in Javascript and comparing values which were truncated. You see this in js, python, etc. constantly.
$ raku -e 'say 9999999999999999.0 - 9999999999999998.0'
1
https://raku.orgThe next representable number after 9999999999999998.0 is 1.0e16. 9999999999999999.0 is not exactly representable in IEEE 754 floats, and will be rounded up or down.
The difference between 0.0 and 2.0 in the table is likely due to different rounding modes. I'm curious how different languages end up with different rounding modes. Is that possible to configure?
julia> 9999999999999999.0 - 9999999999999998.0
2.0
julia> -9.999999999999999 + 9.999999999999998
0.0 $ tclsh
% puts $tcl_version
8.6
% expr "9999999999999999.0-9999999999999998.0"
2.0 with Ada.Text_IO; use Ada.Text_IO;
procedure Example is
begin
Put_Line (Float (9999999999999999.0-9999999999999998.0)'Image);
end Example;
Result: 1.00000E+00
If I really wanted to, I could use a decimal instead, e.g. with Ada.Text_IO; use Ada.Text_IO;
procedure Example is
type Decimal is delta 1.0E-20 digits 38;
begin
Put_Line (Decimal (9999999999999999.0-9999999999999998.0)'Image);
end Example;
Result: 1.00000000000000000000 postgres=# select 9999999999999999.0 - 9999999999999998.0 as result;
result
--------
1.0 select pg_typeof(9999999999999999.0);
-- numeric
select 9999999999999999.0::double precision
- 9999999999999998.0::double precision;
-- 2
More interesting perhaps, is mixing up `real` (aka float32) with `numeric`: select 9999999999999999.0::real - 9999999999999998.0;
-- 272564226 (?! can anyone explain?)
select 9999999999999999.0::real;
-- 10000000300000000
Wat #include<stdio.h>
int main(void) {
long a = (float)9999999999999999;
long b = 9999999999999998;
printf("%ld - %ld = %ld\n", a, b, a-b);
}
produces 10000000272564224 - 9999999999999998 = 272564226For some reason Postgres prints it as `10000000300000000`. It uses a heuristic to print a "pretty" number that converts to the actual stored value, and it's not smart enough to give `10000000000000000`. Some heuristic like this is needed so something like `0.3` doesn't print the actual stored value of `0.300000011920928955078125`, which would be confusing.
You can check all this here: https://www.h-schmidt.net/FloatConverter/IEEE754.html
>>> Decimal("9999999999999999.0") - Decimal("9999999999999998.0")
Decimal('1.0')
perl -Mbignum -E 'say 9999999999999999.0-9999999999999998.0'
This is just me being idiosyncratic I guess, but if I see a number with a decimal place, I default to interpreting it as an fp64 unless otherwise specified - which yields a "correct" answer of 2.0 (which is not an answer I can get to in my head, admittedly)
If the question-asker wanted integer arithmetic, they'd have left off the ".0", and if they wanted something other than fp64 roundTiesToEven they should've been more explicit :P
In particular, 9999999999999999.0 rounds up to 10000000000000000.0
(note, this behavior depends on the rounding mode, https://en.wikipedia.org/wiki/IEEE_754#Rounding_rules )
The answers are all equally "correct" given a particular set of operating rules, the interesting part is what rules they picked.
Edit: See also, https://en.wikipedia.org/wiki/Definitions_of_mathematics - "Mathematics has no generally accepted definition"
So they aren't on the same level, IEEE-754 is not an alternative standard to math. The answers can all be considered correct by a certain definition, but they are not equally correct in our shared context as human beings who know what math is.
59 + 1 can very reasonably be 1:00, for example.
I'm not saying 2.0 is the correct answer, I'm just saying it's not any less correct than 1 is.
(That said, I would never enter an integer with a decimal, and I would think of precision if it was a float. That said that said, there are other ways to bump into that problem which wouldn't make it so obvious - such as dealing with inputs from a user.)
>soup is a programming language I've been working on for a few years that is designed to make it easier for the programmer to write fast, robust programs with no bugs. >Availability >I'm sorry to say soup is not yet available for general use.
Now I am curious what this is.
The result of subtracting 9999999999999998.0 from 9999999999999999.0 is 2.0. This result is due to the limitations in the precision of floating-point arithmetic in computers. When dealing with very large numbers, the precision can be lost, leading to such unexpected results.
I suppose this must be in its training set.
when I ask about this question it generates and runs Python behind the scenes, thus the incorrect answer.
Q: What is the result of subtracting 9999999999999998.0 from 9999999999999999.0
A: The result of subtracting 9999999999999998.0 from 9999999999999999.0 is 1.0. Here is the calculation: 9999999999999999.0−9999999999999998.0=1.0
I also tried a variation and it's still correct, maybe it's using a calculator plugin:
Q: What is the result of 1239995999999999.0 - 1239995999999998.0
A: The result of the subtraction operation 1239995999999999.0−1239995999999998.0 is 1.0
I wonder if there is a way to fool Wolfram in giving the wrong answer. Or ChatGPT?
python 9999999999999999.0 - 9999999999999998.0 Result 2.0
The result of the calculation 9999999999999999.0−9999999999999998.0 is 2.0.
This result may seem counterintuitive, but it's due to how floating-point numbers are represented and handled in computing. Floating-point arithmetic can introduce small rounding errors, especially with very large numbers, as precision is limited. In this case, the precision required to represent these large numbers exactly is beyond what typical floating-point representations (like IEEE 754 double precision) can handle, leading to a slight error in the calculation.
does it say this out loud?