2013-08-09 4 views
1

Что-то странное происходит в моем C-коде ниже.sprintf, кажется, округляет?

Я хочу сравнить числа и I круглые до 4 знаков после запятой.

Я отлажена и может видеть данные, передается в.

Значение tmp_ptr->current_longitude является 6722,31500000, а значение tmp_ptr->current_latitude является 930,0876500000.

После использования заявления Sprintf:

charTmpPtrXPos = "6722.3150" and charTmpPtrYPos = "930.0876". 

Я ожидаю, что точно такие же результаты для speed_info-> myXPos и ​​speed_info-> myYPos, но ни странно, даже если speed_info->myXPos = 6722.31500000 а значение speed_info->myYPos > = 30.0876500000, тем Sprintf заявления

charSpeedPtrYPos= "930.0877" 

Так что в основном оператор sprintf ведет себя по-другому для второго значения и, кажется, округляет его. Отлаживая это, я знаю, что вход в оператор sprintf точно такой же.

Может ли кто-нибудь подумать о причине?

sizeOfSpeedList = op_prg_list_size (global_speed_trajectory); 

tmp_ptr= (WsqT_Location_Message*)op_prg_mem_alloc(sizeof(WsqT_Location_Message)); 
tmp_ptr = mbls_convert_lat_long_to_xy (own_node_objid); 

sprintf(charTmpPtrXPos, "%0.4lf", tmp_ptr->current_longitude); 
sprintf(charTmpPtrYPos, "%0.4lf", tmp_ptr->current_latitude); 

speed_info = (SpeedInformation *) op_prg_mem_alloc (sizeof (SpeedInformation)); 
for (count=0; count<sizeOfSpeedList; count++) 
{ 
    speed_info = (SpeedInformation*) op_prg_list_access (global_speed_trajectory, count); 

    sprintf(charSpeedPtrXPos, "%0.4lf", speed_info->myXPos); 
    sprintf(charSpeedPtrYPos, "%0.4lf", speed_info->myYPos); 

    //if((tmp_ptr->current_longitude == speed_info->myXPos) && (tmp_ptr->current_latitude == speed_info->myYPos)) 
    if ((strcmp(charTmpPtrXPos, charSpeedPtrXPos) == 0) && (strcmp(charTmpPtrYPos, charSpeedPtrYPos) == 0)) 
    { 
     my_speed = speed_info->speed; 
     break; 
    } 
} 
+0

Каковы типы переменных respecive? 'Float'? 'Double'? 'long double'? – glglgl

ответ

0

См: http://www.cplusplus.com/reference/cstdio/printf/

Точка с последующим числом указывает точность, которая в вашем случае 4. Попытайтесь использовать более высокое приглашение, если вам это нужно. Я пробовал ваш номер с точностью 5, и он больше не округляется. Таким образом, это должно быть ...

sprintf(charSpeedPtrYPos, "%0.5lf", speed_info->myYPos); 

..или любое большее количество, которое соответствует вашим потребностям.

4

printf() обычно округляется до ближайшего десятичного представления со связями, отправленными на «четное» (т. Е. Представление, последняя цифра которого равна 0, 2, 4, 6 или 8).

Однако вы должны понимать, что большинство чисел, которые конечно представимы в десятичной форме, не являются конечно представимыми в двоичной с плавающей запятой. Действительное число 930.08765, например, не представляется в виде двоичной с плавающей запятой. То, что у вас действительно есть как значение double (и то, что преобразуется в десятичное), является другим числом, вероятно, немного выше 930.08765, по всей вероятности 930.0876500000000532963895238935947418212890625. Нормально, что это число округляется до десятичного представления 930.0877, поскольку оно ближе к этому представлению, чем к 930.0876.


Обратите внимание, что если вы используете Visual Studio, ваш * Е() функции могут быть ограничены, показывая 17 значащих цифр, мешает вам наблюдать точное значение double ближайшего 930.08765.

+0

Довольно уверен, что вы правы, что число действительно не 930.08765. Я считаю полезным вычесть известное (и «представимое») число, чтобы увидеть дополнительные цифры. См. Http://stackoverflow.com/a/12124973/1967396 для определения того, какие числа вам нужно вычесть ... – Floris

+1

@Floris. Помимо 0,5, дробные числа с плавающей запятой имеют десятичные цифры в конце ... 25 или ... 75. Таким образом, несомненно, что число с плавающей запятой не 930.08765, но действительно помогает иметь функции преобразования, которые работают за пределами первых 17 цифр, хотя бы для того, чтобы увидеть, что такое реальное значение «double». Я бы сделал недосмотр в Visual Studio суевериям, что числа с плавающей запятой не представляют точных значений (они делают. То, для чего вы их используете, является вашей проблемой, но они представляют точно такие значения, как 930.0876500000000532963895238935947418212890625). –

+0

@PascalCuoq: Я бы ограничил ограничение в MSVC более ленивым или ориентированным на производительность, чем суеверие, хотя последнее может сыграть большую роль в неправильных представлениях программистов вокруг него. Написание реализаций 'printf', которые могут дать точный вывод для произвольных значений и которые могут корректно округлить прошлое от ограниченного числа цифр (например, 17), является нетривиальным, и трудно добиться соответствия производительности реализации, ориентированной на ограниченные результаты, без включая две различные реализации и выбор одной из них на основе запрошенной точности. –

2

Это разница между float и double.

ОП, скорее всего, используя tmp_ptr->current_latitude как float и speed_info->myYPos как double. Предложите использовать OP double, хотя пространство и скорость заставляют использовать float.

int main() { 
    float f1 = 930.08765; 
    double d1 = 930.08765; 
    printf("float %0.4f\ndouble %0.4f\n", f1, d1); 
    return 0; 
} 

float 930.0876 
double 930.0877 

Как типичный float использует 4-байтовое представление IEEE двоичной с плавающей точкой, f1 принимает точного значение
930.087646484375
930.0876 (Это 4-значный результат печати)
Этих является самым близким значением float до 930.08765.

Как мудрый, для double, d1 принимает точное значение
930.0876500000000532963895238935947418212890625
930.0877 (Это 4 цифры напечатанный результат)

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


кандидат быстро исправить

sprintf(charSpeedPtrYPos, "%0.4lf", (float) speed_info->myYPos); 

Это первое преобразовать значение из speed_info->myYPos в float. Поскольку не-прототипированные параметры типа float преобразуются в double перед передачей sprintf(), значение будет преобразовано обратно в double. Конечным результатом является потеря точности в числе, но те же результаты преобразования строк.

printf("(float) double %0.4f\n", (float) d1); 
// (float) double 930.0876 

КСТАТИ: l в "%0.4lf" не служит никакой цели генерации кода. Однако это разрешено.

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