-3

In the Tour of Go, step 13, I changed a bit the script

package main

import (
    "fmt"
    "math"
)

func main() {
    var x, y int = 3, 4
    var f float64 = math.Sqrt(float64(x*x + y*y))
    var z uint  = uint(f)
    fmt.Println(x, y, z, f)
}

This returns: 3 4 5 5, fine.

But when I do this change: negating f:

func main() {
    var x, y int = 3, 4
    var f float64 = - math.Sqrt(float64(x*x + y*y))
    var z uint  = uint(f)
    fmt.Println(x, y, z, f)
}

It responds: 3 4 18446744073709551611 -5

Why does the -5.0 being converted to 18446744073709551611 by the uint method?

Isn't there something - an algorithm? - trying to check what it is doing beneath?
It's like the uint method was brutally casting at binary level from float with mantissa and exponent to an integer?

What is happening?

enter image description here

If I am misled, and that methods uint aren't conversion functions like I believed,
but cast methods like you could find (uint)f in C,
what is the conversion function that ensure that:

uint z = anotherFunction(any float64)

will return the positive integer value of that float number?

Marc Le Bihan
  • 2,308
  • 2
  • 23
  • 41
  • 3
    `uint` is not a method, it is a type. `T(x)` is a conversion expression, so `uint(f)` is a conversion that converts the value of `f` to a value of type `uint`. The value range of `uint` is `0` through `18446744073709551615`, note `uint` can't hold negative values, also note the conversion __of non-constant values__ always yields a valid value; there is no indication of overflow. – mkopriva May 26 '23 at 09:18
  • So it doesn't convert. It casts, and therefore produce a wrong value. But what is the method that converts, giving the good value: 5 ? – Marc Le Bihan May 26 '23 at 09:19
  • 3
    Officially it is a [conversion](https://go.dev/ref/spec#Conversions) and Go has no type casting. However feel free to call it whatever you like, just don't expect others to subject themselves to your custom terminology. – mkopriva May 26 '23 at 09:21
  • 3
    No this is a conversion. There are no type casts in Go. This is just how representing unsigned and signed integers work. – Volker May 26 '23 at 09:24
  • It isn't a conversion, because Go is unable to convert it. [|-5.0|] in mathematics isn't 18446744073709551611, it's 5. It's sure. – Marc Le Bihan May 26 '23 at 09:31
  • *"[|-5.0|] in mathematics isn't 18446744073709551611"* -- `uint` isn't a substitute to get the absolute value of a negative number. Absolute value and `uint` are two completely separate concepts. If you want |-5.0| do not use `uint` conversion. @MarcLeBihan – mkopriva May 26 '23 at 09:38
  • What should I use instead to convert a float64 to a uint? – Marc Le Bihan May 26 '23 at 09:43
  • 2
    `uint(math.Abs(f))` @MarcLeBihan – mkopriva May 26 '23 at 09:44
  • 2
    "[|-5.0|] in mathematics isn't 18446744073709551611, it's 5. It's sure." No, this is "sure" only in ℝ or maybe in ℤ. Math is _complicated_. – Volker May 26 '23 at 09:49
  • 3
    If this were really a "cast", the result would be `13840687554816376832`, which is the `float64` bits interpreted as a `uint64`. The conversion _converts_ the number to an integer, then _converts_ it to an unsigned integer. That may seem arbitrary, because it is, as the language needs to make an arbitrary decision how to handle a conversion which is not directly possible. Yes, some conversions may be equivalent to a type cast, but not all. – JimB May 26 '23 at 13:10

1 Answers1

5

Value of f is a negative number: -5.0. What would you expect when you convert this to a value of an unsigned type? Unsigned types have a valid range / representation where there's no room for -5.

Go (like most other languages) uses 2's complement to represent integers. When you convert a signed integer to an unsigned integer, the representation (binary data in memory) is kept, but the number will be interpreted differently.

In general, when converting a negative signed value to an unsigned value, you can calculate the result using:

result: MAX_VALUE + x + 1 = MAX_VALUE - ABS(x) + 1

So for example when converting -5 of type int8 to uint8, the result will be:

255 - 5 + 1 = 251

The same goes for converting -5 of type int64 to uint64:

18446744073709551615 - 5 + 1 = 18446744073709551611

(Note: size of int and uint is platform dependent, on the Go Playground it is of size 64-bit, same as int64 and uint64.)

The last missing piece: your f is of type float64. Converting float64 to integer from Spec: Conversions: Conversions between numeric types:

For the conversion of non-constant numeric values, the following rules apply:

[...]

  1. When converting a floating-point number to an integer, the fraction is discarded (truncation towards zero).

So -5.0 is converted to -5, then the above algorithm can be used how this will look like when interpreted as a value of type uint.

icza
  • 389,944
  • 63
  • 907
  • 827
  • So it isn't a conversion, its a casting. A conversion have for goal: returning the same number. If you tell a mathematician -5.0 as a positive integer has for value 18446744073709551611, he won't trust you. What method will you suggest him to use to convert to uint any float number? – Marc Le Bihan May 26 '23 at 09:24
  • 1
    *"A conversion have for goal: returning the same number."* -- Wrong. See [official language spec](https://go.dev/ref/spec#Conversions): *"A conversion changes the type of an expression to the type specified by the conversion."* @MarcLeBihan – mkopriva May 26 '23 at 09:29
  • But how do you get the correct value, instead? If it's that one you're looking for, for a calculation? If by the mean of `uint` method Go fails, how do you get the value that 99.9999999% of the humanity would expect, except Go? – Marc Le Bihan May 26 '23 at 09:34
  • 1
    Use [`math.Abs`](https://pkg.go.dev/math@go1.20.4#Abs) to get the absolute value (`|-5.0|`). – mkopriva May 26 '23 at 09:35
  • Yes, a `uint(math.Abs(f))` succeeds. – Marc Le Bihan May 26 '23 at 09:44
  • 1
    @MarcLeBihan: A conversion has the **goal** of returning the same value in the new type, but when the goal is not possible (because the same value is not representable in the new type), something else must occur. A cast performs a conversion. – Eric Postpischil May 26 '23 at 11:36
  • 1
    @mkopriva: The text you quote does not indicate it is wrong that the goal of a conversion is to produce the same value in a new type. It merely says the conversion changes the type, not what it does to the value or what the goal value is. – Eric Postpischil May 26 '23 at 11:37