UPDATE 2008-06-25: Here's my current recommendation (the rest is left for historical context).
SSLCipherSuite DHE-RSA-AES256-SHA:EDH-RSA-DES-CBC3-SHA:DHE-RSA-AES128-SHA: AES256-SHA:DES-CBC3-SHA:AES128-SHA:RC4-SHA
--
The completist (Thawte-style)
SSLCipherSuite DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:EDH-RSA-DES-CBC3-SHA: AES256-SHA:DES-CBC3-SHA:AES128-SHA:RC4-SHA:RC4-MD5
The minimalist (Microsoft-style)
SSLCipherSuite AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5
For the Comodo version of the minimalist, swap the order of the 2 AES ciphers.
UPDATE 2008-04-04: Slight change to the minimalist config based on more detailed results from the new tool. This also means that the Comodo config is not what is stated above, but is instead:
The descending minimalist (Comodo-style)
SSLCipherSuite AES256-SHA:AES128-SHA:DES-CBC3-SHA:RC4-SHA:RC4-MD5
Enter the 36 chambers of infrastructure wu-tang
Saturday, March 29, 2008
SSL/TLS cipher selection, with examples and discussion
The last post has examples of some TLS web server crypto configs, but I didn't explain them, particularly why some are better than others. Let's remedy that.
We'll start with some good ones (always start by examining properly packed parachutes!).
Thawte
The completist
grade for www.thawte.com is HIGH
negotiated cipher for www.thawte.com:
DHE-RSA-AES256-SHA
ephemeral keying -> 1024 bits [expires Sat Jan 17 23:59:59 UTC 2009]
valid ciphers for www.thawte.com:
DHE-RSA-AES256-SHA
AES256-SHA
EDH-RSA-DES-CBC3-SHA
DES-CBC3-SHA
DHE-RSA-AES128-SHA
AES128-SHA
RC4-SHA
RC4-MD5
RC4-MD5 (SSLv2)
It is clear that a lot of thought went into the selection of these ciphers and their order of preference (this test only shows us the top choice of the server). Starting at the bottom we have the sole SSLv2 cipher for use by the crustiest old browsers. RC4-MD5 is the strongest, widely deployed, unencumbered SSLv2 cipher, so this is the best choice for backwards compatibility. Next there are 2 more RC4 ciphers (both MD5 versions likely come from the same config statement): the same as the bottom, but in TLSv1/SSLv3 guise, and a slightly improved version with SHA1. Again, these are great choices for balancing the minimum security supported against the need to support as many clients as possible.
The top 6 ciphers can be divided into 3 pairs, with each pair using a strong symmetric cipher with and without ephemeral keying. AES128 and Triple DES (the DES ciphers shown with 168 bit keys) are the most common, high strength, symmetric ciphers in modern browsers. The top cipher (DHE-RSA-AES256-SHA) is not only the strongest cipher you can reasonably expect browsers to support, it's also the most preferred cipher from this server. Although I'd like to see a 2048 bit server key to give a bit more strength to sessions not using the DH ciphers, this is a really clean config.
Great job, Thawte!
Microsoft
The minimalist
grade for www.microsoft.com is MEDIUM
negotiated cipher for www.microsoft.com:
AES128-SHA
server certificate strength is LOW -> 1024 bits [expires Wed Feb 11 18:25:18 UTC 2009]
valid ciphers for www.microsoft.com:
AES256-SHA
DES-CBC3-SHA
AES128-SHA
RC4-SHA
RC4-MD5
RC4-MD5 (SSLv2)
This config is very similar to the one from Thawte (great minds think alike?), with the elimination of the DH ciphers. One point of note is that they have chosen not to make the default cipher the strongest one available. This, again, is likely a conscious choice to balance compatibility, security, and performance. AES128-SHA is a very high security cipher, but it also has good performance on a variety of devices. A 2048 bit server key would make this a perfect, all-purpose config.
I haven't checked whether these ciphers are the defaults for IIS. If they are, even more credit to Microsoft for delivering a default security posture superior to many, competing products. Either way, good job!
As a side note, Comodo has a very good, very similar configuration, but the difference makes it less useful as an example.
UPDATE 2008-03-30: This is a really useful little post about IE ciphers. Good reading!
And now, some not so good ones.
Facebook
The kitchen sink
grade for www.facebook.com is LOW
negotiated cipher for www.facebook.com:
RC4-MD5
server certificate strength is LOW -> 1024 bits [expires Tue Sep 28 23:53:12 UTC 2010]
valid ciphers for www.facebook.com:
DHE-RSA-AES256-SHA
AES256-SHA
EDH-RSA-DES-CBC3-SHA
DES-CBC3-SHA
DHE-RSA-AES128-SHA
AES128-SHA
RC4-SHA
RC4-MD5
RC4-MD5 (SSLv2)
EDH-RSA-DES-CBC-SHA
DES-CBC-SHA
EXP-EDH-RSA-DES-CBC-SHA
EXP-DES-CBC-SHA
EXP-RC2-CBC-MD5
EXP-RC2-CBC-MD5 (SSLv2)
EXP-RC4-MD5
EXP-RC4-MD5 (SSLv2)
Yikes! I have to think they didn't put much thought into this configuration. It looks like they either went with the Apache default config (which is a bloated mess), or just turned on almost every option and (mostly) called it a day. There is no good reason to include any export ciphers, much less lots of export ciphers (if anyone has solid data supporting or contradicting that assertion, I'd really love to see it). Those 56 bit DES ciphers are similarly antiquated and have no business in there.
The worst part is that, having thrown in every cipher they could find, they set the default to RC4-MD5, a medium security cipher. Facebook has chosen to take the same cipher our good examples above use as a last-ditch fallback for TLSv1/SSLv3 negotiation and made it their preferred cipher. Adding insult to injury is that 1024 bit server key.
A little thought and planning would go a long way.
Bank of America
The heist
grade for www.bankofamerica.com is LOW
negotiated cipher for www.bankofamerica.com:
RC4-MD5
server certificate strength is LOW -> 1024 bits [expires Sat Jan 17 23:59:59 UTC 2009]
valid ciphers for www.bankofamerica.com:
DES-CBC3-SHA
RC4-MD5
RC4-MD5 (SSLv2)
DES-CBC-SHA
It looks like someone spent a little time thinking about what to do here, and then decided on the wrong things. The 56 bit DES cipher serves no purpose, there are no AES-based ciphers, and the server default cipher is, as with Facebook, a medium security cipher best used as a fallback. Finally, they have the traditional, 1024 bit server key to round things out.
I'd like to say I expect better from a bank. Instead, all I can say is that I hope for better from a bank. I won't hold my breath.
Yes, gentle reader, the state of affairs in the rarefied world of SSL/TLS web server configuration is pretty rotten. I know we can do better than this. The problems are not technical, but social. Vendors must ship products with sane defaults. Companies who run secure web servers must take due care in configuring them. Customers of those companies should pressure them to fix things that are broken and then take their business elsewhere if problems persist.
setec astronomy
We'll start with some good ones (always start by examining properly packed parachutes!).
Thawte
The completist
grade for www.thawte.com is HIGH
negotiated cipher for www.thawte.com:
DHE-RSA-AES256-SHA
ephemeral keying -> 1024 bits [expires Sat Jan 17 23:59:59 UTC 2009]
valid ciphers for www.thawte.com:
DHE-RSA-AES256-SHA
AES256-SHA
EDH-RSA-DES-CBC3-SHA
DES-CBC3-SHA
DHE-RSA-AES128-SHA
AES128-SHA
RC4-SHA
RC4-MD5
RC4-MD5 (SSLv2)
It is clear that a lot of thought went into the selection of these ciphers and their order of preference (this test only shows us the top choice of the server). Starting at the bottom we have the sole SSLv2 cipher for use by the crustiest old browsers. RC4-MD5 is the strongest, widely deployed, unencumbered SSLv2 cipher, so this is the best choice for backwards compatibility. Next there are 2 more RC4 ciphers (both MD5 versions likely come from the same config statement): the same as the bottom, but in TLSv1/SSLv3 guise, and a slightly improved version with SHA1. Again, these are great choices for balancing the minimum security supported against the need to support as many clients as possible.
The top 6 ciphers can be divided into 3 pairs, with each pair using a strong symmetric cipher with and without ephemeral keying. AES128 and Triple DES (the DES ciphers shown with 168 bit keys) are the most common, high strength, symmetric ciphers in modern browsers. The top cipher (DHE-RSA-AES256-SHA) is not only the strongest cipher you can reasonably expect browsers to support, it's also the most preferred cipher from this server. Although I'd like to see a 2048 bit server key to give a bit more strength to sessions not using the DH ciphers, this is a really clean config.
Great job, Thawte!
Microsoft
The minimalist
grade for www.microsoft.com is MEDIUM
negotiated cipher for www.microsoft.com:
AES128-SHA
server certificate strength is LOW -> 1024 bits [expires Wed Feb 11 18:25:18 UTC 2009]
valid ciphers for www.microsoft.com:
AES256-SHA
DES-CBC3-SHA
AES128-SHA
RC4-SHA
RC4-MD5
RC4-MD5 (SSLv2)
This config is very similar to the one from Thawte (great minds think alike?), with the elimination of the DH ciphers. One point of note is that they have chosen not to make the default cipher the strongest one available. This, again, is likely a conscious choice to balance compatibility, security, and performance. AES128-SHA is a very high security cipher, but it also has good performance on a variety of devices. A 2048 bit server key would make this a perfect, all-purpose config.
I haven't checked whether these ciphers are the defaults for IIS. If they are, even more credit to Microsoft for delivering a default security posture superior to many, competing products. Either way, good job!
As a side note, Comodo has a very good, very similar configuration, but the difference makes it less useful as an example.
UPDATE 2008-03-30: This is a really useful little post about IE ciphers. Good reading!
And now, some not so good ones.
The kitchen sink
grade for www.facebook.com is LOW
negotiated cipher for www.facebook.com:
RC4-MD5
server certificate strength is LOW -> 1024 bits [expires Tue Sep 28 23:53:12 UTC 2010]
valid ciphers for www.facebook.com:
DHE-RSA-AES256-SHA
AES256-SHA
EDH-RSA-DES-CBC3-SHA
DES-CBC3-SHA
DHE-RSA-AES128-SHA
AES128-SHA
RC4-SHA
RC4-MD5
RC4-MD5 (SSLv2)
EDH-RSA-DES-CBC-SHA
DES-CBC-SHA
EXP-EDH-RSA-DES-CBC-SHA
EXP-DES-CBC-SHA
EXP-RC2-CBC-MD5
EXP-RC2-CBC-MD5 (SSLv2)
EXP-RC4-MD5
EXP-RC4-MD5 (SSLv2)
Yikes! I have to think they didn't put much thought into this configuration. It looks like they either went with the Apache default config (which is a bloated mess), or just turned on almost every option and (mostly) called it a day. There is no good reason to include any export ciphers, much less lots of export ciphers (if anyone has solid data supporting or contradicting that assertion, I'd really love to see it). Those 56 bit DES ciphers are similarly antiquated and have no business in there.
The worst part is that, having thrown in every cipher they could find, they set the default to RC4-MD5, a medium security cipher. Facebook has chosen to take the same cipher our good examples above use as a last-ditch fallback for TLSv1/SSLv3 negotiation and made it their preferred cipher. Adding insult to injury is that 1024 bit server key.
A little thought and planning would go a long way.
Bank of America
The heist
grade for www.bankofamerica.com is LOW
negotiated cipher for www.bankofamerica.com:
RC4-MD5
server certificate strength is LOW -> 1024 bits [expires Sat Jan 17 23:59:59 UTC 2009]
valid ciphers for www.bankofamerica.com:
DES-CBC3-SHA
RC4-MD5
RC4-MD5 (SSLv2)
DES-CBC-SHA
It looks like someone spent a little time thinking about what to do here, and then decided on the wrong things. The 56 bit DES cipher serves no purpose, there are no AES-based ciphers, and the server default cipher is, as with Facebook, a medium security cipher best used as a fallback. Finally, they have the traditional, 1024 bit server key to round things out.
I'd like to say I expect better from a bank. Instead, all I can say is that I hope for better from a bank. I won't hold my breath.
Yes, gentle reader, the state of affairs in the rarefied world of SSL/TLS web server configuration is pretty rotten. I know we can do better than this. The problems are not technical, but social. Vendors must ship products with sane defaults. Companies who run secure web servers must take due care in configuring them. Customers of those companies should pressure them to fix things that are broken and then take their business elsewhere if problems persist.
setec astronomy
The sorry state of SSL/TLS operational best practice
A recent discussion on the TLS Working Group list got me curious about how well folks are doing at configuring SSL/TLS on their "secure" web servers. I put together a simple tool to help answer the question, and the the results are pretty grim. Little adoption of the ciphers that use ephemeral keys, widespread use of known-bad export ciphers, almost universal use of 1024 bit RSA keys for server certificates, etc.
Without discussion, here are some results:
Amazon.com
Microsoft
Comodo
PayPal
Facebook
Thawte
Verisign
Note: Verisign will not negotiate TLSv1
Bank of America
GMail
CIA
Amazon.com
grade for www.amazon.com is LOW
negotiated cipher for www.amazon.com:
RC4-MD5
server certificate strength is LOW -> 1024 bits [expires Wed Sep 17 23:59:59 UTC 2008]
valid ciphers for www.amazon.com:
AES256-SHA
DES-CBC3-SHA
AES128-SHA
RC4-SHA
RC4-MD5
RC4-MD5 (SSLv2)
DES-CBC-SHA
EXP-DES-CBC-SHA
EXP-RC2-CBC-MD5
EXP-RC2-CBC-MD5 (SSLv2)
EXP-RC4-MD5
EXP-RC4-MD5 (SSLv2)
Microsoft
grade for www.microsoft.com is MEDIUM
negotiated cipher for www.microsoft.com:
AES128-SHA
server certificate strength is LOW -> 1024 bits [expires Wed Feb 11 18:25:18 UTC 2009]
valid ciphers for www.microsoft.com:
AES256-SHA
DES-CBC3-SHA
AES128-SHA
RC4-SHA
RC4-MD5
RC4-MD5 (SSLv2)
Comodo
grade for www.comodo.com is MEDIUM
negotiated cipher for www.comodo.com:
AES256-SHA
server certificate strength is LOW -> 1024 bits [expires Mon Jun 28 23:59:59 UTC 2010]
valid ciphers for www.comodo.com:
AES256-SHA
DES-CBC3-SHA
AES128-SHA
RC4-SHA
RC4-MD5
RC4-MD5 (SSLv2)
PayPal
grade for www.paypal.com is LOW
negotiated cipher for www.paypal.com:
RC4-MD5
server certificate strength is LOW -> 1024 bits [expires Thu Jan 29 23:59:59 UTC 2009]
valid ciphers for www.paypal.com:
AES256-SHA
DES-CBC3-SHA
AES128-SHA
RC4-SHA
RC4-MD5
RC4-MD5 (SSLv2)
DES-CBC-SHA
EXP-DES-CBC-SHA
EXP-RC4-MD5
EXP-RC4-MD5 (SSLv2)
grade for www.facebook.com is LOW
negotiated cipher for www.facebook.com:
RC4-MD5
server certificate strength is LOW -> 1024 bits [expires Tue Sep 28 23:53:12 UTC 2010]
valid ciphers for www.facebook.com:
DHE-RSA-AES256-SHA
AES256-SHA
EDH-RSA-DES-CBC3-SHA
DES-CBC3-SHA
DHE-RSA-AES128-SHA
AES128-SHA
RC4-SHA
RC4-MD5
RC4-MD5 (SSLv2)
EDH-RSA-DES-CBC-SHA
DES-CBC-SHA
EXP-EDH-RSA-DES-CBC-SHA
EXP-DES-CBC-SHA
EXP-RC2-CBC-MD5
EXP-RC2-CBC-MD5 (SSLv2)
EXP-RC4-MD5
EXP-RC4-MD5 (SSLv2)
Thawte
grade for www.thawte.com is HIGH
negotiated cipher for www.thawte.com:
DHE-RSA-AES256-SHA
ephemeral keying -> 1024 bits [expires Sat Jan 17 23:59:59 UTC 2009]
valid ciphers for www.thawte.com:
DHE-RSA-AES256-SHA
AES256-SHA
EDH-RSA-DES-CBC3-SHA
DES-CBC3-SHA
DHE-RSA-AES128-SHA
AES128-SHA
RC4-SHA
RC4-MD5
RC4-MD5 (SSLv2)
Verisign
Note: Verisign will not negotiate TLSv1
grade for www.verisign.com is LOW
negotiated cipher for www.verisign.com:
RC4-MD5
server certificate strength is LOW -> 1024 bits [expires Fri May 08 23:59:59 UTC 2009]
valid ciphers for www.verisign.com:
DES-CBC3-SHA
RC4-MD5
RC4-MD5 (SSLv2)
DES-CBC-SHA
EXP-RC2-CBC-MD5
EXP-RC2-CBC-MD5 (SSLv2)
EXP-RC4-MD5
EXP-RC4-MD5 (SSLv2)
Bank of America
grade for www.bankofamerica.com is LOW
negotiated cipher for www.bankofamerica.com:
RC4-MD5
server certificate strength is LOW -> 1024 bits [expires Sat Jan 17 23:59:59 UTC 2009]
valid ciphers for www.bankofamerica.com:
DES-CBC3-SHA
RC4-SHA
RC4-MD5
RC4-MD5 (SSLv2)
DES-CBC-SHA
GMail
grade for www.gmail.com is MEDIUM
negotiated cipher for www.gmail.com:
AES256-SHA
server certificate strength is LOW -> 1024 bits [expires Thu May 15 17:24:01 UTC 2008]
valid ciphers for www.gmail.com:
AES256-SHA
DES-CBC3-SHA
AES128-SHA
RC4-SHA
RC4-MD5
RC4-MD5 (SSLv2)
DES-CBC-SHA
EXP-DES-CBC-SHA
EXP-RC2-CBC-MD5
EXP-RC2-CBC-MD5 (SSLv2)
EXP-RC4-MD5
EXP-RC4-MD5 (SSLv2)
CIA
grade for www.cia.gov is LOW
negotiated cipher for www.cia.gov:
RC4-SHA
server certificate strength is LOW -> 1024 bits [expires Sat Feb 12 23:59:59 UTC 2011]
valid ciphers for www.cia.gov:
RC4-SHA
RC4-MD5
RC4-MD5 (SSLv2)
EXP-RC4-MD5
EXP-RC4-MD5 (SSLv2)
Wednesday, March 26, 2008
BigDecimal: mostly acceptable alternative to Float
I forgot to mention in yesterday's extended whine about Ruby floating point that there is a package in the standard library called BigDecimal that is a mostly good alternative to the normal Float class. Here is the simplest way to use it:
The second line adds a new method, to_d, to String, Float, and Rational. Back to my annoying example of broken floating point math:
Well, almost. BigDecimal slows things down a bit compared to Float and the extra work (the remembering part, not the typing part) of adding to_d is effort that shouldn't be required. I should be clear here that I have no problem with BigDecimal itself. BigDecimal is extremely useful, as are the extended precision libraries for other languages. No, the problem remains that, even though the Ruby Core team could implement a reasonable compromise in Float so it behaves consistently, they refuse to.
require "bigdecimal"
require "bigdecimal/util"
The second line adds a new method, to_d, to String, Float, and Rational. Back to my annoying example of broken floating point math:
>> require "bigdecimal"
=> true
>> require "bigdecimal/util"
=> true
>> (9.54 / 0.001)
=> 9540.0
>> (9.54 / 0.001).to_i
=> 9539
>> (9.54 / 0.001).to_d.to_i
=> 9540
Well, almost. BigDecimal slows things down a bit compared to Float and the extra work (the remembering part, not the typing part) of adding to_d is effort that shouldn't be required. I should be clear here that I have no problem with BigDecimal itself. BigDecimal is extremely useful, as are the extended precision libraries for other languages. No, the problem remains that, even though the Ruby Core team could implement a reasonable compromise in Float so it behaves consistently, they refuse to.
rb_spread patch for Ruby 1.8.recent/1.9 and Spread 4.0
Spread is a group communication system, developed at Johns Hopkins, based on Virtual Synchrony and Extended Virtual Synchrony. Lots of neat stuff therein. The Ruby API hasn't been updated since 2005, though, and will not compile against recent versions of Ruby 1.8 or 1.9, nor is it compatible with the 4.0 release of Spread.
I put together this patch to update the API to the current goodness and make the test and sample programs actually work. My apologies for not doing lots of #ifdefs to make it work with both Spread 3.x and 4.x. I have only tested this on OS X 10.5 on i386. Success or failure, please let me know your experiences on other platforms.
I put together this patch to update the API to the current goodness and make the test and sample programs actually work. My apologies for not doing lots of #ifdefs to make it work with both Spread 3.x and 4.x. I have only tested this on OS X 10.5 on i386. Success or failure, please let me know your experiences on other platforms.
Tuesday, March 25, 2008
Floating point arithmetic, bug reports, and monkey patching
It turns out that Barbie was right and math actually is hard. At least, math is hard if you adhere to IEEE floating point spec. C adheres to that spec. Languages built on C inherit that adherence and, even though they could fix some of the problems that come with it, they leave things broken.
In the case of Ruby, they also tell you there is no problem. And it is fixed in the next version. With this monkey patch. But I'm getting ahead of myself.
Here's Ruby (1.8.6 on OS X 10.5) in action:
Well, that seems straightforward enough. 9540 is certainly the right answer. I need the integer representation, not the floating point version, though, so we'll just call the handy Float#to_i function:
Don't feel badly, I blinked several times and had a few more sips of beer before I realized what I was seeing. A second ago, the value was 9540, but now it is 9539. I'll just bang on it a bit to see what I've done wrong:
Yup. By converting the value to a String and then converting that String to an Integer we get the right value back. Sometimes Ruby admits to the underlying IEEE spec edge case, other times it hides it. Sadly, Python behaves similarly (though we are alerted to a problem immediately, rather than having it sneak up on us later):
Yikes! And, frowny face, the other trouble is here, too:
I wrote a simple C program to play directly with the underlying types. Here are two variants that illustrate the problem clearly:
So, I filed a bug against Ruby. Turns out someone else filed a similar bug a few weeks ago. Both of our bugs were rejected on the grounds that things were working as they were supposed to. I continued to argue that, regardless of what the underlying data types are doing, Ruby could and should do better. We went back and forth a bit, and the Ruby gent finally noticed I filed the bug against 1.9.
Why does that matter? Well, it seems that, even though it totally, definitely, under no circumstances is a bug, they've added functionality to Float#round that, among other things, means it is possible to mostly work around the problem. You can now pass it an argument giving the rounding precision desired. So now we can do this (Ruby 1.9 on OS X 10.5):
Our grumpy Ruby Core gent was even kind enough to provide a monkey patch to Float to provide a truncate method that actually worked reliably:
Since this is absolutely not a bug, the workaround (I can't call it a fix since it introduces problems with overflow) will no doubt remain a hack. A hack to make Float behave correctly. That's a darned shame, Ruby folks.
In the case of Ruby, they also tell you there is no problem. And it is fixed in the next version. With this monkey patch. But I'm getting ahead of myself.
Here's Ruby (1.8.6 on OS X 10.5) in action:
>> (9.54 / 0.001)
=> 9540.0
>>
Well, that seems straightforward enough. 9540 is certainly the right answer. I need the integer representation, not the floating point version, though, so we'll just call the handy Float#to_i function:
>> (9.54 / 0.001).to_i
=> 9539
>>
Don't feel badly, I blinked several times and had a few more sips of beer before I realized what I was seeing. A second ago, the value was 9540, but now it is 9539. I'll just bang on it a bit to see what I've done wrong:
>> (9.54 / 0.001).to_s.to_i
=> 9540
>>
Yup. By converting the value to a String and then converting that String to an Integer we get the right value back. Sometimes Ruby admits to the underlying IEEE spec edge case, other times it hides it. Sadly, Python behaves similarly (though we are alerted to a problem immediately, rather than having it sneak up on us later):
>>> (9.54 / 0.001)
9539.9999999999982
>>>
Yikes! And, frowny face, the other trouble is here, too:
>>> str(9.54 / 0.001)
'9540.0'
>>> int(9.54 / 0.001)
9539
>>>
I wrote a simple C program to play directly with the underlying types. Here are two variants that illustrate the problem clearly:
#include
int
main()
{
double f = 9.54 / 0.001;
printf("%f %i\n", f, (int)f);
return 0;
}
$ ./test
9540.000000 9539
$
#include
int
main()
{
double f = 9.54 / 0.001;
printf("%f %i\n", f, (int)(float)f);
return 0;
}
$ ./test
9540.000000 9540
$
So, I filed a bug against Ruby. Turns out someone else filed a similar bug a few weeks ago. Both of our bugs were rejected on the grounds that things were working as they were supposed to. I continued to argue that, regardless of what the underlying data types are doing, Ruby could and should do better. We went back and forth a bit, and the Ruby gent finally noticed I filed the bug against 1.9.
Why does that matter? Well, it seems that, even though it totally, definitely, under no circumstances is a bug, they've added functionality to Float#round that, among other things, means it is possible to mostly work around the problem. You can now pass it an argument giving the rounding precision desired. So now we can do this (Ruby 1.9 on OS X 10.5):
irb(main):002:0> (9.54 / 0.001).round(2).to_i
=> 9540
irb(main):003:0>
Our grumpy Ruby Core gent was even kind enough to provide a monkey patch to Float to provide a truncate method that actually worked reliably:
class Float
def truncate_rounding(figs)
if figs > 0
n = 10 ** figs
(self * n).round / n
else
n = 10 ** -figs
(self / n).round / 10 * (n * 10)
end
end
end
Since this is absolutely not a bug, the workaround (I can't call it a fix since it introduces problems with overflow) will no doubt remain a hack. A hack to make Float behave correctly. That's a darned shame, Ruby folks.
Subscribe to:
Posts (Atom)
About Me
Blog Archive
-
▼
2008
(22)
-
▼
March
(6)
- Recommended SSLCipherSuite configurations for Apache
- SSL/TLS cipher selection, with examples and discus...
- The sorry state of SSL/TLS operational best practice
- BigDecimal: mostly acceptable alternative to Float
- rb_spread patch for Ruby 1.8.recent/1.9 and Spread...
- Floating point arithmetic, bug reports, and monkey...
-
▼
March
(6)