LoginSignup
2
2

More than 5 years have passed since last update.

require 'mathn' するとARのdatetimeの精度で死ぬ件

Last updated at Posted at 2016-11-24

簡単に書くと、こういうことです。

mysql> show create table items \G
*************************** 1. row ***************************
       Table: items
Create Table: CREATE TABLE `items` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
irb(main):002:0> Item.new(name: 'foo').save
   (0.2ms)  BEGIN
  SQL (0.4ms)  INSERT INTO `items` (`name`, `created_at`, `updated_at`) VALUES ('foo', '2016-11-24 15:14:34', '2016-11-24 15:14:34')
   (0.8ms)  COMMIT
=> true
irb(main):003:0> require 'mathn'
=> true
irb(main):004:0> Item.new(name: 'foo').save
   (0.3ms)  BEGIN
  SQL (0.3ms)  INSERT INTO `items` (`name`, `created_at`, `updated_at`) VALUES ('foo', '2016-11-24 15:14:55.147015', '2016-11-24 15:14:55.147015')
   (0.6ms)  COMMIT
=> true

なにが起きたかおわかりだろうか?
require 'mathn'した後にcreated_atへのINSERTには小数点以下がついています。

スクリーンショット 2016-11-25 0.32.37.png

何が起きたか?

MySQL5.6以降、datetimeでミリ秒がサポートされるようになったが、何も考えずにTime.newのミリ秒以下までINSERT文に含めると問題が多数発生するため、精度を見て小数点以下を付けたり付けなかったりするようになった。

精度を見て、小数点以下を削除するコードは以下の通り。

def apply_seconds_precision(value)
  return value unless precision && value.respond_to?(:usec)
  number_of_insignificant_digits = 6 - precision
  round_power = 10**number_of_insignificant_digits
  value.change(usec: value.usec / round_power * round_power)
end

さて、通常Rubyでは123456 / 1000000 / 1000000の結果は以下のようになる:

irb(main):002:0> 123456 / 1000000 / 1000000
=> 0

これがmathnrequireするとどうなるかというと

irb(main):004:0> 123456 / 1000000 / 1000000
=> (1929/15625000000)

こうなります。

なんでこれがdatetimeの精度に影響するかというと、

  value.change(usec: value.usec / round_power * round_power)

この部分ですね。

割り算で小数点以下を消しているのですが、mathnrequireすると、正確な計算をしようとするため、小数点以下が消えないのです。

なんでこうなるの?

class Fixnum
  remove_method :/

  ##
  # +/+ defines the Rational division for Fixnum.
  #
  #   1/3  # => (1/3)

  alias / quo
end

恐ろしい、恐ろしい。
筋トレと野菜が必要ですね。

関連

Feature #10169: It might be better to make Mathn class deprecated - Ruby trunk - Ruby Issue Tracking System

2
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2