浮動小数点数の値が整数であるかどうかを調べる(C++, Ruby, JS, Go)

浮動小数点数が整数であるかどうかを調べたいことがあった。

で。

いくつか方法があるので紹介する。

# ruby

## `to_i` を使う

こんな感じ。

```ruby:ruby
def integer?(x)
  x==x.to_i;
end
```

なお。`Integer` に変換できない値を入力すると例外になる。

こんな感じ。

```ruby:ruby
(Float::INFINITY).to_i #=> FloatDomainError: Infinity
(Float::NAN).to_i #=> FloatDomainError: NaN
```

それと。`to_i` メソッドは `Rational` や `Integer` にも生えているのでそういう型の値を上記の関数に食べさせてもちゃんと動く。

あと。
ここでは `to_i` を使ったけど、`ceil`, `round`, `truncate` でも同じ結果になる。
ちなみに。
C++の `std::floor(double)` は `double` を返すけど、ruby の `Float#floor` は、`Integer` を返す。要注意。

実行速度も測ってみたけど、大差ない感じ。ソースコードを見ると `to_i` が速そうではある。

ちなみに `to_i` の実装はこんな風になっているらしい：

```c:to_i
static VALUE
flo_to_i(VALUE num)
{
    double f = RFLOAT_VALUE(num);

    if (f > 0.0) f = floor(f);
    if (f < 0.0) f = ceil(f);

    return dbl2ival(f);
}
```

なお。
ruby の `Integer` は多倍長整数を扱えるので、前述の `integer?` は入力がひどく大きな値でも `true`を返す。

そういうのが困る方のために次の節。

## 例えば 32bit 符号あり整数になるかどうかが知りたい場合

32bit じゃなくてもいいんだけど、そういう固定長の整数と等しい値かどうかが知りたいこともある。
そういうときにどうするか。

### 比較する

前述の `integer?` があるんだから、大小比較すればよいというアイディア。
こんな感じ。

```ruby:ruby
def is_int32_1(x)
  integer?(x) && (-2**31)<=x && x<=(2**31-1)
end
```

なんか `2**31` とか書くのがちょっと気分悪い。そういう方のために次の節。

### `pack` / `unpack` を使う

```ruby:ruby
def is_int32_2(x)
  x==[x].pack('l').unpack1('l')[0]
end
```

なんかこれはこれで迂遠な感じだけど、どうだろう。

他にいい方法があるかなぁ。

## 実行結果

ここまで紹介した３つの方法の実行結果は下表の通り：


|入力|to_i|int32(比較)|int32(pack/unpack)|
|:--|:--:|:--:|:--:|
|0.0|true|true|true|
|1.0|true|true|true|
|1.5|false|false|false|
|2e19|true|false|false|
|9e19/7|true|false|false|
|NAN|例外|例外|例外|
|INF|例外|例外|例外|
|-0.0|true|true|true|
|1.5r|false|false|false|
|1r|true|true|true|
|9e19.to_r/7|false|false|false|

いずれの実装も非数や無限が例外になる。
それと。`integer?(9e19/7)` が `true` になるのは `Float` の仮数部が 53bit しかないから。

# C++ / C

まあ C でも C++ でも似たような感じになるけど、とりあえず C++ で書くことにする。

## `std::floor` を使う

こんな感じ。

```c++:c++
#include <cmath>
bool is_integer( double x ){
  return std::floor(x)==x;
}
```

`std::floor` が返すのは `double` ( 入力が `float` なら `float`、`long double` なら `long double`) なので、`is_integer(1e100)` は `true` になることに注意。

それと。
上記の例では `std::floor` を使ったけど、`std::ceil`, `std::round`, `std::trunc`, `std::rint`, `std:: nearbyint` でも同様。

しかしやたらたくさんある。

## std::modf を使う

`std::modf` を使うという手もある。

こんな感じ。

```c++:c++
bool is_integer_modf( double x ){
  double r;
  return 0==std::modf(x, &r);
}
```

手元の処理系では、`std::modf(無限, 略)` は `0` を返すので、`is_integer_modf(無限)` は `true` になる。

## `std::lround` を使う

---

2020.6.13 追記。
以下の `lround` を使った実装はダメ。詳しくは [コメント]( https://qiita.com/Nabetani/items/3280a08340aeececfe33#comment-214b611d274bd172b98f )参照。

---

C++11 以降には `std::lround` という関数がある。`std::round` 等と異なり、`long` を返す。
これを使うと `long` に入る範囲内の整数かどうかを調べることができる。
こんな感じ。

```c++:c++
#include <cmath>
bool is_int_lround(double x){
  return std::lround(x)==x;
}
```

なお。`int` とか `std::int32_t` とかを返す同等の関数はない模様。

## 例えば 32bit 符号あり整数になるかどうかが知りたい場合

---

2020.6.13 追記。
なんか「これしか無いと思う」とか書いちゃってるけど、以下の `static_cast` を使った実装未定義動作を含んでるのでダメです。
詳しくは [浮動書数点数の値を整数に変換する(C, C++)]( https://qiita.com/Nabetani/items/d4302b6ea3c630890326 ) を参照のこと。

---

C++ の場合、32bit 整数にキャストができるので、実装は簡単。

```c++:c++
#include <cstdint>
bool is_int32( double x ){
  return static_cast<std::int32_t>(x)==x;
}
```

まあこれしか無いと思う。

## 結果

ここまでの結果をまとめると下表のようになる。


|入力|floor|modf|lround|int32_t|
|:--|:--:|:--:|:--:|:--:|
|0.0|true|true|true|true|
|1.0|true|true|true|true|
|1.5|false|false|false|false|
|1e18|true|true|true|false|
|2e19|true|true|false|false|
|9e19/7|true|true|false|false|
|NAN|false|false|false|false|
|INF|true|true|false|false|
|-0.0|true|true|true|true|

手元の処理系では long が 64bit なので、上記の通り `1e18` を入力した場合の動作が `lround` 版と `int32_t` 版で異なる。

# JavaScript

## `Math.floor` を使う

こんな感じ。

```javascript:JavaScript
function is_integer(x) {
  return x == Math.floor(x);
}
```

大きな数や 無限 を入れると true になる。

こちらも　`Math.ceil()`, `Math.round()`, `Math.trunc()` でも同じ。

## bit or 演算子を使う

bit or 演算子を使うと急に 32bit 符号あり整数に変換されるという不思議な機能があるので、これを使えばいい。

こんな感じ。

```javascript:JavaScript
function is_int_bitor(x) {
  return x == (x|0);
}
```

JavaScript に慣れてないので、ソースコード的には割と気持ち悪い感じがする。慣れればそうでもないんだと思う。

## 例えば 32bit 符号なし整数になるかどうかが知りたい場合

ruby と C++ では符号ありだったけど、JavaScript の 32bit 符号ありはもう bit or 演算子でやってしまったので、今回は符号なし。

### TypedArray を使う

実装はこんな感じ。

```javascript:JavaScript
function is_uint32_typedarray(x) {
  return (new Uint32Array([x]))[0]==x;
}
```

今どき `Uint32Array` が使えない環境も少ないと思うけど、そういう不幸な方は次の節を。

### 比較する

ruby で行ったように、整数かどうか判断した上で値が uint32 の範囲に入っているかどうか調べるという手もある。

こんな感じ。

```javascript:JavaScript
function is_uint32_compare(x) {
  return is_integer(x) && 0<=x && x<=4294967295;
}
```

マジカルな 4294967295 が書かれているのが気分悪い。
ちなみに `(1<<32)-1` の値は `0` になる模様。
`x<Math.pow(2,32)` と書いたほうわかりやすいかもしれない。

## 結果

結果は下表の通り：

|入力|floor|bit or|Uint32Array|uint32(比較)|
|:--|:--:|:--:|:--:|:--:|
|0.0|true|true|true|true|
|1.0|true|true|true|true|
|1.5|false|false|false|false|
|1e18|true|false|false|false|
|2e19|true|false|false|false|
|NAN|false|false|false|false|
|INF|true|false|false|false|
|-0.0|true|true|true|true|
|2147483648|true|false|true|true|
|2147483647|true|true|true|true|
|-2147483649|true|false|false|false|
|-2147483648|true|true|false|false|

# Go

## `math.Floor` を使う

普通に。こんな感じ。

```go:go
func isInteger(x float64) bool {
	return math.Floor(x) == x
}
```

`math.Ceil`, `math.Round`, `math.Trunc`, `math.RoundToEven` でも同じ結果になる。
というか。RoundToEven なんてあったんだ。知らなかった。

## `math.Modf` を使う

C++ に `std::modf` があったが、go には `math.Modf` がある。

こんな感じ。

```go:go
func isIntegerModf(x float64) bool {
	_, frac := math.Modf(x)
	return frac == 0
}
```

C++ の(手元の処理系の) std::modf と異なり、go の `math.Modf(無限)` は (無限,非数) を返すので、`isIntegerModf(無限)` は `false` になる。

## 例えば 32bit 符号なし整数になるかどうかが知りたい場合

2021.4.26 追記

以下で「キャストすればよい」となっているが、
[The Go Programming Language Specification - The Go Programming Language]( https://golang.org/ref/spec#Conversions ) を見ると、浮動小数点数から整数への変換は、まず

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

とした上で

> In all non-constant conversions involving floating-point or complex values, if the result type cannot represent the value the conversion succeeds but the result value is implementation-dependent.

となっている。つまり、

```go
f := math.Pow(2, 33)
v := uint32(f)
```

の v の値は実装依存で、go 言語の仕様からはわからない、ということらしい。
とはいえこれは未定義動作ではなく「値は実装依存」なので、下記のコードは正しく動作すると思う。
ちょっと自信がないけれど。

---

C/C++ と同様、キャストすればよい。

こんな感じ。

```go:go
func isInt32(x float64) bool {
	return float64(int32(x)) == x
}
```

## 結果

結果は下表の通り：

|入力|floor|modf|int32|
|:--|:--:|:--:|:--:|
|0.0|true|true|true|
|1.0|true|true|true|
|1.5|false|false|false|
|1e18|true|true|false|
|2e19|true|true|false|
|NAN|false|false|false|
|INF|true|false|false|
|-0.0|true|true|true|
|2147483648|true|true|false|
|2147483647|true|true|true|
|-2147483649|true|true|false|
|-2147483648|true|true|true|

# というわけで

というわけで、符号小数点数が整数かどうかを調べる方法はたくさんある。
状況に応じて最適なものを選ぶのがよいけど、無駄な計算をしないように注意したい。