NaN や INF を int に変換したらどうなるか調べてみた

浮動小数点の非数や無限を int に変換したらどうなるのかを、いろいろな処理系で。

環境は主に macOS Mojave 10.14.2。
cl.exe(C言語) と C# だけは windows 10(64bit)。
いずれも CPU は x64。

ARM でどうなるのかも興味あるけど手元に環境がない。
だれか記事書きませんか？

# C言語

ソースコードはこんな

```c:c99
#include <stdio.h>

double div(double x, double y ){
  return x/y;
}

int main()
{
  double nan = div(0.0,0.0);
  double pinf = div(1.0, 0.0);
  double minf = -pinf;
  printf("%f | %f | %f |\n", nan, pinf, minf);
  printf("%d | %d | %d |\n", (int)nan, (int)pinf, (int)minf);
  return 0;
}
```

感じ。

|処理系|NaN|+INF|-INF|
|:--|--:|--:|--:|
|clang-1000.11.45.5|-2147483648 | -2147483648 | -2147483648 |
|gcc-8 (Homebrew GCC 8.2.0) 8.2.0|-2147483648 | -2147483648 | -2147483648 |
|cl.exe 19.16.27025.1|-2147483648 | -2147483648 | -2147483648 |

C言語的には「未定義動作」だと思う。

clang と gcc については 2015年の MacBookPro なので、x64。
cl.exe(VisualStudio)も x64。x86 ではビルドしなかった。

ちなみに `div` が関数になっているのは `double nan = 0.0/0.0;` は cl.exe でコンパイルエラーになるから。コンパイル時にはエラーになるけど実行時には nan になる。

#ruby

ソースコードはこんな

```ruby:ruby
def toi(f)
  begin
    f.to_i
  rescue => e
    e.inspect.gsub( /\W+/, " " ).strip
  end
end

values = [Float::NAN, Float::INFINITY, -Float::INFINITY]
puts values.map{ |f| toi(f).inspect }.join("|")
```

感じ。

|処理系|NaN|+INF|-INF|
|:--|--:|--:|--:|
|ruby 2.5.3p105 |"FloatDomainError NaN"|"FloatDomainError Infinity"|"FloatDomainError Infinity"|
|jruby 9.2.0.0 (2.5.0)|"FloatDomainError NaN"|"FloatDomainError Infinity"|"FloatDomainError Infinity"|
意外と例外になる。

jruby では、当然 ruby 合わせで、java(後述)とは違う結果になる。当たり前か。

# python

ソースコードはこんな

```python:python2or3
import sys
import re
import numpy as np

def toi(f):
  try:
    return int(f)
  except:
    return re.sub( r"\W+", " ", str(sys.exc_info()[0]) )

nan = float("nan")
pinf = float("inf")
minf = -pinf
ints = [ toi(x) for x in [ nan, pinf, minf ] ]
print( "|".join( ints ) )

npa = np.array([nan, pinf, minf])
print( npa.astype("int32"))
```

感じ。

|処理系|手段|NaN|+INF|-INF|
|:--|:--|--:|--:|--:|
|Python 3.7.1|int()|class ValueError | class OverflowError | class OverflowError |
|Python 2.7.15|int()|type exceptions ValueError | type exceptions OverflowError | type exceptions OverflowError |
|Python 3.7.1|.astype("int32")|-2147483648|-2147483648|-2147483648|
|Python 2.7.15|.astype("int32")|-2147483648|-2147483648|-2147483648|

int() で作ると例外。まあそうだよね。
numpy だと、例外なんて投げているヒマがない。C言語と同じになった。

# go

ソースコードはこんな

```go:go
package main

import (
	"fmt"
	"math"
)

func main() {
	nan := math.NaN()
	pinf := math.Inf(1)
	minf := math.Inf(-1)
	fmt.Printf("%d|%d|%d|\n", int32(nan), int32(pinf), int32(minf))
}
```

感じ。

|処理系|NaN|+INF|-INF|
|:--|--:|--:|--:|
|go1.11.2 darwin/amd64|-2147483648|-2147483648|-2147483648|

panic にするのはやりすぎだし、例外はないので C言語みたいに変な値にするしかない。

[Go プログラミング言語仕様](http://golang.jp/go_spec#Conversions) によると

> 浮動小数点値または複素数を含んでいるすべての非定数変換において、結果となる型が値を表現することができなければ、変換自体は成功しますが、結果となる値は実装依存となります。

ということで、実装依存らしい。

# `C#`

ソースコードはこんな

```c#:C#
using System;

namespace NanToInt
{
    class M
    {
        static void Main()
        {
            double nan = double.NaN;
            double pinf = double.PositiveInfinity;
            double minf = double.NegativeInfinity;
            int inan = (int)nan;
            int ipinf = (int)pinf;
            int iminf = (int)minf;
            Console.WriteLine( "{0}|{1}|{2}|", inan, ipinf, iminf );
        }
    }
}
```

感じ。

|処理系|NaN|+INF|-INF|
|:--|--:|--:|--:|
|csc 2.10.0.0|-2147483648|-2147483648|-2147483648|

例外にはならずに、clang や gcc と同じ値になる。

ちなみに
`(decimal)double.NaN`
とやると、やや不適切なメッセージの
「`System.OverflowException: Decimal 型の値が大きすぎるか、または小さすぎます。`」
という例外になる。

#Java

ソースコードはこんな

```Java:Java1.8
class NanToIntTest {
  public static void main( String[] args ){
    int inan = (int)Double.NaN;
    int ipinf = (int)Double.POSITIVE_INFINITY;
    int iminf = (int)Double.NEGATIVE_INFINITY;
    System.out.printf("%d|%d|%d\n", inan, ipinf, iminf);
  }
}
```

感じ。

久々に「`public static void main`」って書いた。

|処理系|NaN|+INF|-INF|
|:--|--:|--:|--:|
|java version "1.8.0_60"|0|2147483647|-2147483648|

Java は例外にはならない。
clang, gcc や go とは違う値になる。

ちなみに `new BigDecimal(Double.NaN);` は「`java.lang.NumberFormatException: Infinite or NaN`」という例外になる。

# groovy

ソースコードはこんな

```groovy
int inan = (int)Double.NaN;
int ipinf = (int)Double.POSITIVE_INFINITY;
int iminf = (int)Double.NEGATIVE_INFINITY;
printf("%d|%d|%d\n", inan, ipinf, iminf);
```

感じ。

|処理系|NaN|+INF|-INF|
|:--|--:|--:|--:|
|Groovy Version: 2.5.4 JVM: 1.8.0_60|0|2147483647|-2147483648|

やはり Java と同じ結果になる。

# JavaScript(nodejs)

```javascript:node-v11.3.0
var values = [0.0 / 0.0, 1.0 / 0.0, -1.0 / 0.0]
console.log(["or op."].concat(values.map(x => x | 0)).join("|"))
console.log(["and op."].concat(values.map(x => x & 0xffffffff)).join("|"))
function u16a(x){
  a = new Uint16Array(1);
  a[0] = x;
  return a[0];
}
function i16a(x){
  a = new Int16Array(1);
  a[0] = x;
  return a[0];
}
console.log(["u16a"].concat(values.map(x=>u16a(x))).join("|"))
console.log(["i16a"].concat(values.map(x=>i16a(x))).join("|"))
```

処理系は node.js の v11.3.0。

|手段|NaN|+INF|-INF|
|:--|--:|--:|--:|
|or op.|0|0|0|
|and op.|0|0|0|
|u16a|0|0|0|
|i16a|0|0|0|

32bit の整数にしようとしたり、ビット数の限られた整数型に突っ込んだりすると、ゼロになる。
`math.floor(x)` みたいなことをすると `NaN` とか `Infinity` になるので面白くない。

# Rust

ソースコードはこんな

```rust:rust
fn main() {
    let nan = std::f64::NAN;
    let pinf = std::f64::INFINITY;
    let minf = -std::f64::INFINITY;
    println!("{}|{}|{}", nan as i32, pinf as i32, minf as i32);
}
```

感じ。

意外と例外にならない。結果は

|処理系|NaN|+INF|-INF|
|:--|--:|--:|--:|
|rustc 1.30.1|-2147483648|-2147483648|-2147483648|

なんだけど、[型間のキャスト]( https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/casting-between-types.html ) によると

> **注意: 現在、丸められた値がキャスト先の整数型で扱えない場合、このキャストは未定義動作を引き起こします。** これには Inf や NaN も含まれます。 これはバグであり、修正される予定です。

とのことで、**修正** されるらしい。

# PHP

ソースコードはこんな

```php:php7
<?php
$nan = 0.0/0.0;
$pinf = 1.0/0.0;
$minf = -1.0/0.0;
echo( join("|", [ (int)$nan, (int)$pinf, (int)$minf ])."\n" );
echo( join("|", [ intval($nan), intval($pinf), intval($minf) ])."\n" );
?>
```

感じでいいのかしら。わかりません。

結果は意外にも

|処理系|手段|NaN|+INF|-INF|
|:--|:--|--:|--:|--:|
|PHP 7.1.19|(int)|0|0|0|
|PHP 7.1.19|intval()|0|0|0|

と、全部ゼロになる。

#perl

ソースコードはこんな

```perl:perl
use strict;
use warnings;

my $pinf = 9**9**9;
my $nan = $pinf - $pinf;
my $minf = -$pinf;

my $inan = 0|$nan;
my $ipinf = 0|$pinf;
my $iminf = 0|$minf;

printf "%x|%x|%x\n", $inan, $ipinf, $iminf;
```

感じかな。

結果は下表の通り：

|処理系|NaN|+INF|-INF|
|:--|--:|--:|--:|
|perl v5.18.2|0|ffffffffffffffff|8000000000000000|

わかりにくい値になっている。

`int x` だと nan のままになったりするので面白くない。

# Swift

生まれて初めて Swift を書いてみたものの、うまく書けず敗北。

```swift
print(
    Int(Double.nan), "|",
    Int(Double.infinity), "|",
    Int(-Double.infinity), "|")
```

実行すると

```text
Fatal error: Double value cannot be converted to Int because it is either infinite or NaN
```

となる。（先頭の1行のみ出したけど、本当は10行以上のエラーメッセージが出る）

このエラーをキャッチしてエラーの種別を出力したかったんだけど、キャッチする方法がわからず敗退。
Swift 難しい。

とにかく。Swift の場合は NaN や INF を `Int()` で整数にしようとすると「`Fatal error`」になるらしい。恐ろしい。

なお。
Swift は `xcrun swift` で実行していて、 `xcrun swift --version` で出てくるバージョンは

```text
Apple Swift version 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1)
Target: x86_64-apple-darwin18.2.0
```

となっている。

# fortran

fortran2003ぐらいなのかな。よくわからないまま書いてみた。こんな

```fortran:fortran
function div(a,b)
  real a, b
  div = a/b
end function

program main
  real :: nan
  real :: pinf
  real :: minf
  nan = div(0.0,0.0)
  pinf = div(1.0,0.0)
  minf = -pinf
  print *, int(nan), "|", int(pinf), "|", int(minf)
end
```

感じ。合ってる？
環境は GNU Fortran (Homebrew GCC 8.2.0) 8.2.0。

結果は

|処理系|NaN|+INF|-INF|
|:--|--:|--:|--:|
|gfortran8.2| -2147483648 | -2147483648 | -2147483648|

と、C言語と同じ。CPU 依存なのかなぁ。

# Dart

生まれて初めて Dart を書いてみた。

```dart:dart
import 'dart:io';

toIntStr( n ){
  try{
    return n.toInt().toString();
  }
  catch(e){
    return  "exception";
  }
}

void main() {
  stdout.write(toIntStr(double.nan));
  stdout.write("|");
  stdout.write(toIntStr(double.infinity));
  stdout.write("|");
  stdout.write(toIntStr(double.negativeInfinity));
  print("|");
}
```

こんな感じ。
結果は

|処理系|NaN|+INF|-INF|
|:--|--:|--:|--:|
|Dart VM version: 2.1.0| exception|exception|exception|

例外は
「`Unsupported operation: Infinity or NaN toInt`」
という内容。

# Haskell

ソースコードはこんな

```haskell
nan = 0.0/0.0
pinf = 1.0/0.0
minf = -pinf
toint :: (Double->Int)->Double->Int
toint f x = f(x)

table t f = 
  "|"++t++"|"++
  (show $ toint f nan)++"|"++
  (show $ toint f pinf)++"|"++
  (show $ toint f minf)++"|"

main = do
  putStrLn $ table "round" round
  putStrLn $ table "truncate" truncate
  putStrLn $ table "ceiling" ceiling
  putStrLn $ table "floor" floor
```

感じ。

処理系は `The Glorious Glasgow Haskell Compilation System, version 8.4.4`。

実行すると

|関数|NaN|+INF|-INF|
|:--|--:|--:|--:|
|round|0|0|0|
|truncate|0|0|0|
|ceiling|0|0|0|
|floor|0|0|0|

と、意外とゼロになる。
例外とかはないんだっけ。

# まとめ

表にした。

|処理系|手段|NaN|+INF|-INF|
|:--|:--|--:|--:|--:|
|C99 on amd64|キャスト|-2147483648 | -2147483648 | -2147483648 |
|ruby 2.5|.to_i|例外|例外|例外|
|python|int()|例外|例外|例外|
|python|numpy の .astype|-2147483648|-2147483648|-2147483648|
|go on amd64|int32()|-2147483648|-2147483648|-2147483648|
|C#|(int)|-2147483648|-2147483648|-2147483648|
|Java1.8|(int)|0|2147483647|-2147483648|
|Groovy(JVM1.8)|(int)|0|2147483647|-2147483648|
|JavaScript(nodejs)|or op.|0|0|0|
|JavaScript(nodejs)|Uint16Array|0|0|0|
|Rust on amd64|as i32|-2147483648|-2147483648|-2147483648|
|PHP 7.1.19|(int), intval()|0|0|0|
|perl5.18|or op.|0|ffffffffffffffff|8000000000000000|
|Swift4.2.1|Int()|Fatal error|Fatal error|Fatal error|
|gfortran8.2|int()| -2147483648 | -2147483648 | -2147483648|
|Dart2.1| .toInt()|例外|例外|例外|
|Haskell(GHC8.4)|round等|0|0|0|

|結果|処理系|
|:--|:--|
| -2147483648 になる|C99, numpy, go ,Rust, gfortran (すべて amd64)|
| 例外になる|ruby, python, Dart|
| 0 になる|JavaScript, PHP7, Haskell|
| Fatal error になって死ぬ|Swift4|
| その他|Java, Groovy, Perl5|

という具合かな。

# とまあ

とまあいろいろ試してみた。