# Numeric types

In Fable, we use F# numeric types, which are all translated to JS Number (64-bit floating type) at the exception of `int64`

, `uint64`

, `bigint`

and `decimal`

.

Fable numbers are very nearly compatible with .NET semantics, but translating into Javascript types has consequences:

- (non-standard) All floating point numbers are implemented as 64 bit (
`double`

). This makes`float32`

numbers more accurate than expected. - (non-standard) Arithmetic integers of 32 bits or less are implemented with different truncation from that expected, as whole numbers embedded within
`double`

. - (OK) Conversions between types are correctly truncated.
- (OK) Bitwise operations for 64 bit and 32 bit integers are correct and truncated to the appropriate number of bits.
- (non-standard) Bitwise operations for 16 bit and 8 bit integers use the underlying JavaScript 32 bit bitwise semantics. Results are not truncated as expected, and shift operands are not masked to fit the data type.
- (OK) Longs have a custom implementation which is identical in semantics to .NET and truncates in 64 bits, although it is slower.

32 bit integers thus differ from .NET in two ways:

- Underlying 52 bit precision, without expected truncation to 32 bits on overflow. Truncation can be forced if needed by
`>>> 0`

. - On exceeding 52 bits absolute value floating point loses precision. So overflow will result in unexpected lower order 0 bits.

The loss of precision can be seen in a single multiplication:

`((1 <<< 28) + 1) * ((1 <<< 28) + 1) >>> 0`

The multiply product will have internal double representation rounded to `0x0100_0000_2000_0000`

. When it is truncated to 32 bits by `>>> 0`

the result will be `0x2000_0000`

not the .NET exact lower order bits value of `0x2000_0001`

.

The same problem can be seen where repeated arithmetic operations make the internal (non-truncated) value large. For example a linear congruence random number generator:

`let rng (s:int32) = 10001*s + 12345`

The numbers generated by repeated application of this to its result will all be even after the 4th pseudo-random number, when `s`

value exceeds 2^53:

```
let rec randLst n s =
match n with
| 0 -> [s]
| n -> s :: randLst (n-1) (rng s)
List.iter (printfn "%x") (randLst 7 1)
```

The resulting printed list of pseudo-random numbers does not work in Fable:

Fable | .NET |
---|---|

1 | 1 |

574a | 574a |

d524223 | d524223 |

6a89e98c | 6a89e98c |

15bd0684 | 15bd0685 |

3d8b8000 | 3d8be20e |

50000000 | 65ba5527 |

0 | 2458c8d0 |

## Workarounds

- When accurate low-order bit arithmetic is needed and overflow can result in numbers larger than 2^53 use
`int64`

,`uint64`

, which use exact 64 bits, instead of`int32`

,`uint32`

. - Alternately, truncate all arithmetic with
`>>> 0`

or`>>> 0u`

as appropriate before numbers can get larger than 2^53:`let rng (s:int32) = 10001*s + 12345 >>> 0`

## Printing

One small change from .NET in `printf`

, `sprintf`

, `ToString`

. Negative signed integers are printed in hexadecimal format as sign + magnitude, in .NET they are printed as two's complement bit patterns.