# How does your programming language handle “minus zero” (-0.0)?

The ubiquitous IEEE floating-point standard defines two numbers to represent zero, the positive and the negative zeros. You also have the positive and negative infinity. If you compute the inverse of the positive zero, you get the positive infinity. If you compute the inverse of the negative zero, you get the negative infinity.

It seems that programming languages handle these values in vastly different ways.

Though the C++ standard probably says very little about negative zeros, in practice I am getting the results I would expect. In C++ (GNU GCC and LLVM clang), the following code prints out (-inf, inf, -inf) :

```double minus_zero = -0.0;
double plus_zero = +0.0;
double parsed = strtod("-0.0", nullptr);
std::cout <<  1.0/minus_zero << std::endl;
std::cout <<  1.0/plus_zero << std::endl;
std::cout <<  1.0/parsed << std::endl;
```

Java also produces what I expect (-Infinity, Infinity, -Infinity) from the following code sample:

```double minus_zero = -0.0;
double plus_zero = +0.0;
double parsed = Double.valueOf("-0.0");
System.out.println(1/minus_zero);
System.out.println(1/plus_zero);
System.out.println(1/parsed);
```

Swift also seems to do what I expect (-inf, inf, -inf) :

```var minus_zero = -0.0
var plus_zero = +0.0
var parsed = Double("-0.0")!
print(1/minus_zero)
print(1/plus_zero)
print(1/parsed)
```

The Go programming language seems to be unaware of negative zeros when parsing literal constants. Thus the following code will print out +Inf, +Inf.

```var minus_zero = -0.0
var plus_zero = 0.0
fmt.Println(1/minus_zero)
fmt.Println(1/plus_zero)
```

C# is aware of negative infinity, but when parsing strings the behaviour depends on your version. With .NET 5, the following code prints out what I expect (-infinity, infinity, -infinity) but with previous versions you may get -infinity, infinity, infinity.

```double minus_zero = -0.0;
double plus_zero = +0.0;
double parsed = Double.Parse("-0.0");
Console.WriteLine(1/minus_zero);
Console.WriteLine(1/plus_zero);
Console.WriteLine(1/parsed);
``` ### Daniel Lemire

A computer science professor at the University of Quebec (TELUQ).

## 22 thoughts on “How does your programming language handle “minus zero” (-0.0)?”

1. Bruce says:

FWIW Rust also produces what you would expect:

```fn main() { let minus_zero = -0.0; let plus_zero = 0.0; let parsed = "-0.0".parse::<f64>().unwrap(); println!("{}", 1.0/minus_zero); println!("{}", 1.0/plus_zero); println!("{}", 1.0/parsed); } ```

Output:

```-inf inf -inf ```

2. Josh Bleecher Snyder says:

minus_zero := 0.0
minus_zero *= -1

3. Kali says:

JavaScript:

```const plusZero = 0; const minusZero = -0; const parsedMinus = parseInt("-0"); console.log({ minusZero: 1/minusZero, // -Infinity plusZero: 1/plusZero, // Infinity parsedMinus: 1/parsedMinus // -Infinity }); ```

1. William says:

let parsedMinus = parseFloat(“-0.0”)
console.log(1/parsedMinus) // -Infinity

4. Stefan Kanthak says:

Depending on the compiler and the optimisation option used, your test program measures compile time or runtime behaviour — which can but differ.
For C and C++, you should but declare the doubles as volatile and feed a volatile char xxx[] = “-0.0”; to strtod()

1. Daniel Lemire says:

Are you aware of a compiler where the result of strtod would be computed at compile time?

Specifically, a compiler where the following function would become trivial…

``````bool touch() {
return strtod("-0.0", nullptr) == 0;
}
``````

I’d be very interested in knowing about such a system.

1. Stefan Kanthak says:

Evaluation of the expression strtod(constant, NULL) during compile time is a legitimate optimisation any C/C++ compiler is allowed to perform. There are C/C++ compilers which evaluate for example signbit(constant), log(constant), strlen(constant) or strcmp(constant, constant) etc., so why shouldn’t they not (be able to) evaluate strtod(constant, NULL) or strtol(constant, NULL, 0) too? The compiler already has the parser for the numbers, these library functions are part of the language and their semantics well-defined! Unless you can prove or safely assume that no compiler will ever optimize these expressions it’s better to exercise defensive programming.

1. Daniel Lemire says:

I do not doubt that in all the examples provided in my blog post, an optimizing compiler is allowed to just memoize the output. Of course, it should ensure that the result is undistinguishable from what would happen if you were to run it without optimization. But I have never observe an optimizing compiler that would optimize away strtod and I’d be very interested in knowing about such a scenario.

More to the point of your reply… Is your expectation that by marking the string as volatile, thus precluding some optimizations, we will get different results ?

1. Stefan Kanthak says:

I still remember your blog post https://lemire.me/blog/2020/06/26/gcc-not-nearest/

The interpretation of floating point numbers during compile time may differ from run-time.
I just fed the following snippet to the (ancient) GCC on your EPYC system “Rome” and to LLVM 10.0:

```int main() { double minus_0 = -0.0; double plus_0 = +0.0; return 1.0/minus_0 == 1.0/plus_0; } ```

gcc -O3 -o- -s demo.c

```main: movsd .LC0(%rip), %xmm0 xorl %eax, %eax movl \$0, %edx movapd %xmm0, %xmm1 divsd .LC2(%rip), %xmm0 divsd .LC1(%rip), %xmm1 ucomisd %xmm0, %xmm1 setnp %al cmovne %edx, %eax ret .align 8 ```

.LC0:
.long 0
.long 1072693248
.align 8
.LC1:
.long 0
.long -2147483648
.align 8
.LC2:
.long 0
.long 0
.ident “GCC: (GNU) 8.3.1 20190311 (Red Hat 8.3.1-3)”

clang -O3 -o- -s demo.c

```main: # @main xorl %eax, %eax retq .ident "AMD clang version 10.0.0 (CLANG: AOCC_2.2.0-Build#93 2020_06_25) (based on LLVM Mirror.Version.10.0.0)" ```

You see the difference?

2. Stefan Kanthak says:

1. no, I don’t know a compiler which optimises strtod(“-0-0”, 0) and evaluates it at compile time (but some which optimise strcmp(constant, constant) for example);
2. I expect that a volatile char zero[] = “-0.0”; strtod(zero, 0); should disable that optimisation — both GCC and clang but bail out with a warning “passing ‘volatile char *’ to parameter of type ‘const char *’ discards qualifiers”

1. Stefan Kanthak says:

AFAIK the D language/compiler sports a similar feature and allows to evaluate code during compilation.

5. Andreas Davour says:

fn main() {
let minus = -0.0;
let plus = 0.0;

```println!("{}", 1.0/minus); println!("{}", 1.0/plus); ```

}

\$ rustc minus.rs
\$ ./minus
-inf
inf

6. Arun says:

Same behaviour with D as well.

```void main() { import std.stdio, std.conv; double minus_zero = -0.0; double plus_zero = +0.0; double parsed = to!double("-0.0");```

``` writeln(1/minus_zero); writeln(1/plus_zero); writeln(1/parsed); } ```

```\$ ldc2 a.d \$ ./a -inf inf -inf \$ ```

7. Thomas Müller Graf says:

Related to https://github.com/golang/go/issues/30951 : I assume in most programming languages the equivalent of “double x = -0” is the same as “double x = 0”. Meaning: -0 is interpreted as an integer (and, so, 0, as there is no negative 0 integer), which is then converted to a double.

1. Daniel Lemire says:

Yes, but from what I can see, they will opt to at least warn you.

8. Marshall Ward says:

Negative zeros are one of the issues that we have to handle in our bit reproducibility tests. When we are unable to justify a sign, we will do something like `x = x + 0.0` which converts -0.0 into +0.0. And I think that this is the correct behavior via IEEE754. But I also don’t know if we can count on all platforms to do this.

9. ajdude says:

I just tried it in Ada.

```with Ada.Float_Text_IO; use Ada.Float_Text_IO;```

``` ```

```procedure Main is minus_zero : Float := -0.0; plus_zero : Float := +0.0; parsed : Float := Float'Value("-0.0"); begin Put(1.0/minus_zero); Put(1.0/plus_zero); Put(1.0/parsed); end Main; ```

Gave me “-Inf”, “+Inf”, “-Inf”

10. amos says:

Someone needs to send this to the team working on Apple’s iPhone Weather App

11. Serinx says:

Zero in math is kind of dumb. 1/0 should equal 1. Zero is null or nill or no thing.

12. Sitwon says:

Isn’t division by zero undefined? I would expect the result to be NaN, if not an error.

1. Daniel Lemire says:

Isn’t division by zero undefined?

It is in the sense that it is not part of the real numbers, but here we are defining an extended set which includes -infinity and +infinity.

Whether that’s wise to do so is another story.

You may subscribe to this blog by email.