我正在以编程方式计算给定音符的频率。
快速介绍:
C3 = B3 x 2 ^ (1/12)
在Ruby中编写这个公式,我想出了以下内容:
# Starting from the note A4 (440Hz)
A4 = 440.to_f
# I want the frequencies of each note over the next 3 octaves
number_of_octaves = 3
# There are 12 semitones per octave
SEMITIONES_PER_OCTAVE = 12
current_freq = A4
(number_of_octaves * SEMITIONES_PER_OCTAVE).times do |i|
puts "-----" if i % 12 == 0 # separate each octave with dashes
puts current_freq
current_freq = current_freq * 2 ** Rational('1/12')
end
我回来的结果并不完美。 A票据似乎略高于预期:
-----
440.0
466.1637615180899
493.8833012561241
523.2511306011974
554.3652619537443
587.3295358348153
622.253967444162
659.2551138257401
698.456462866008
739.988845423269
783.9908719634989
830.6093951598906
-----
880.0000000000003
932.3275230361802
987.7666025122486
1046.502261202395
1108.7305239074888
1174.6590716696307
1244.5079348883241
1318.5102276514804
1396.9129257320162
1479.9776908465383
1567.981743926998
1661.2187903197814
-----
1760.000000000001
1864.6550460723606
1975.5332050244976
2093.0045224047904
2217.4610478149784
2349.3181433392624
2489.0158697766497
2637.020455302962
2793.825851464034
2959.9553816930784
3135.963487853998
3322.4375806395647
注意A频率 - 而不是880,1760,它们稍微更高。
我认为Ruby的Rational应该提供准确的计算并避免使用浮点数的舍入错误。
任何人都可以解释一下:
答案 0 :(得分:5)
我不清楚在这个表达式中:current_freq * 2 ** Rational('1/12')
Ruby是否将整个计算保留在Rational领域。在Ruby中,你得到:
2.0.0p195 :001 > current_freq = 440
=> 440
2.0.0p195 :002 > current_freq * 2 ** Rational('1/12')
=> 466.1637615180899
计算产生一个浮点数,而不是一个Rational。如果我们保持Rational,它看起来像:
2.0.0p195 :005 > Rational( current_freq * 2 ** Rational('1/12'))
=> (4100419809895505/8796093022208)
即使你这样做:
2.0.0p195 :010 > Rational(2) ** Rational(1,12)
=> 1.0594630943592953
Ruby从Rational转向浮动。关于Rational的Ruby文档并没有清楚地描述这一点,但是给出的例子在将一个有理的分数表示为非整数时表明了这一点。这是有道理的,因为当你将一个有理数给一个有理数(分数,非整数)指数时,你很可能得到一个无理数。 2**(1/12)
就是其中一例。
因此,为了保持准确性,您需要将所有内容保留在Rational领域中,一旦达到无理数,就无法实现。正如Scott Hunter建议的那样,你可以通过一些自定义函数来缩小字段以控制不准确性。目前还不清楚在这种情况下是否值得付出努力。
答案 1 :(得分:0)
虽然我确定它正好代表1/12(作为一个分数),但是一旦你将它用作指数,你就会回到浮点状态,并且可能会回归。
我想你编写自己的幂函数,它检查指数是否为整数并明确使用乘法;至少可以照顾你的A。
答案 2 :(得分:0)
回答问题的第二部分:
如何改进上述代码以获得真正准确的结果?
你可以用 f = 2 n / 12 ×440 来计算频率:
def freq(n)
2 ** (n/12.0) * 440
end
puts (0..12).map { |n| freq(n) }
输出:
440.0
466.1637615180899
493.8833012561241
523.2511306011972
554.3652619537442
587.3295358348151
622.2539674441618
659.2551138257398
698.4564628660078
739.9888454232688
783.9908719634985
830.6093951598903
880.0