2015-03-09 2 views
3

У меня есть очень простая функция для преобразования температуры от ˚C TO ˚K.Swift: несогласованность двойного преобразования. Как правильно сравнивать парные пары?

func convertKelvinToCelsius(temp:Double) ->Double { 
     return temp - 273.15 
} 

И у меня есть модульный тест для управления этой функцией. Здесь проблема:

func testKelvinToCelsius(){ 
      var check1 = conv.convertKelvinToCelsius(200.00) // -73.149999999999977 
      var check2 = 200.00 - 273.15      // -73.149999999999977 
      var check3 = Double(-73.15)      // -73.150000000000006 

      //Passes 
      XCTAssert(conv.convertKelvinToCelsius(200.00).description == Double(-73.15).description, "Shoud convert from celsius kelvin") 

      //Fails 
      XCTAssert(conv.convertKelvinToCelsius(200.00) == Double(-73.15), "Shoud convert from celsius kelvin") 
    } 

При добавлении точки останова и проверьте значения Check1, Check2 и check3, они очень интересны:

check1 Double -73.149999999999977 
check2 Double -73.149999999999977 
check3 Double -73.150000000000006 

Вопросы:

  1. Почему Swift возвращает разные значения для проверки1/check2 и check3

  2. Как можно Я получаю второй тест, чтобы пройти, потому что писал его, как будто я испытал запахи test1. Зачем мне нужно преобразовывать парные разряды в строки, чтобы иметь возможность сравнивать их?

  3. Наконец, когда я println check1, check2 и check3, все они печатаются как '-73.15'. Зачем? Почему бы не печатать точно, а не путать программистов !?

Репродуцировать:

Просто введите 200 - 273.15 == -73.15 в вас площадка и смотреть его идти false !!

+0

[Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой] (http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). –

+0

Возможный дубликат [Сбой с плавающей запятой?] (Http://stackoverflow.com/questions/588004/is-floating-point-math-broken). - Даже если этот вопрос был первоначально о JavaScript, проблема такая же, как у C или Swift (и многих других языков). –

ответ

5

Ожидаемое поведение для значений с плавающей запятой. They cannot be 100% accurately represented.

Вы можете использовать функцию XCTAssertEqualWithAccuracy, чтобы утверждать, что значения с плавающей запятой находятся в заданном диапазоне друг от друга.

Причина, по которой println печатает одно и то же значение для всех, состоит в том, что она внутренне округляет их до двух десятичных знаков (я полагаю).

+0

XCTAssertEqualWithAccuracy работал. благодаря! – Shaunak

+0

извините забрал. Мой тест проходит, но я все еще пытаюсь понять, почему проверка 2 и проверка 3 будет оценивать несколько разные значения. – Shaunak

+0

Опять же, это связано с тем, что эти цифры не могут быть представлены точно внутри. Цифры '200.00' и' 237.15' не являются точными числами, вы, вероятно, увидите это, если будете рассматривать их как отдельные переменные. Небольшие различия здесь складываются, и поэтому check2 и check3 не равны. – Rengers

2

Это не особая проблема, связанная с тем, как десятичные числа создаются на компьютерах и какова их точность. Вам нужно будет работать с DBL_EPSILON.

1

Swift, как и большинство языков, использует двоичные числа с плавающей запятой.

С двоичными числами с плавающей запятой некоторые цифры могут быть представлены точно, но большинство из них не могут. То, что может быть представлено точно, это целые числа, если они не очень велики (например, 100000000000000.0 в порядке), и такие целые числа умножаются или делятся на две (7,755, это 59,0/8, но 7.3 нет).

Каждая операция с плавающей запятой дает точный результат, округленный до ближайшего числа с плавающей запятой. Итак, вы получаете

200.0 -> Exactly 200 
273.15 -> A number very close to 273.15 
200 - 273.15 -> A number very close to -73.15 
-73.15 -> A number very close to -73.15 

Если вы сравниваете два числа, которые очень близки к -73.15, они не обязательно равны. Это не проблема оператора ==; что они будут правильно определять, являются ли они равными или нет. Проблема в том, что эти два номера могут быть разными.

Смежные вопросы