Использование floorf для уменьшения количества десятичных знаков

Я хотел бы использовать для вычислений первые пять цифр числа. Например, число с плавающей запятой: 4.23654897E-05

Я хочу использовать 4.2365E-05. Я пробовал следующее

#include <math.h>
#include <stdio.h>

float num = 4.23654897E-05;
int main(){
float rounded_down = floorf(num * 10000) / 10000;
printf("%f",rounded_down);
return 0;

}

На выходе будет 0,000000. Желаемый результат - 4,2365E-05. Короче говоря, для хранения мантиссы выделено 52 бита. Есть ли способ уменьшить количество выделяемых битов?

Есть предложения, как это можно сделать?


person Natasha    schedule 30.01.2018    source источник
comment
сначала проверьте это.   -  person Sourav Ghosh    schedule 30.01.2018
comment
Как название имеет какое-либо отношение к вопросу? Не спрашивают о выделении памяти? Название должно быть кратким однострочным изложением того, о чем вы спрашиваете. В противном случае это довольно бесполезно.   -  person Some programmer dude    schedule 30.01.2018
comment
Взгляните на Округление числа до двух десятичных знаков в C и тому подобное. Для этого Google достаточно просто. Фактически, я только что заметил, что ссылка на него находится в правой части этой страницы и была предложена вам, когда вы разместили свой вопрос. приобретите привычку проверять эти предложения перед публикацией.   -  person Mawg says reinstate Monica    schedule 30.01.2018
comment
Я думаю, тебе стоит попробовать% E вместо% f.   -  person No Em    schedule 30.01.2018
comment
OP, вероятно, хочет округлить число до 5 действительных цифр.   -  person Marian    schedule 30.01.2018
comment
обратите внимание, что 4.23654897E-05 не является числом с плавающей запятой и не может быть точно представлен в виде числа с плавающей запятой одинарной точности (который имеет только ~ 7 цифр точности)   -  person phuclv    schedule 30.01.2018
comment
Выведите 4.23654897E-05*10000 и объясните, почему, по вашему мнению, floor следует округлить так, как вы хотите.   -  person n. 1.8e9-where's-my-share m.    schedule 30.01.2018
comment
Или просто напечатайте 4.23654897E-05 в первую очередь ...   -  person Lundin    schedule 30.01.2018
comment
Почему вы хотите использовать только пять цифр? Отказ от информации обычно бесполезен. Вы пытаетесь исправить ошибки округления? Правильный ответ зависит от вашей конечной цели. Лучшие результаты обычно получаются, если хранить все доступные биты до конца.   -  person Eric Postpischil    schedule 30.01.2018
comment
Часть вашего вопроса требует сокращения десятичных цифр. Часть спрашивает о сокращении битов. Что ты действительно хочешь? Расщепление Veltkamp-Dekker уменьшает бит. Это отличается от округления до десятичных цифр.   -  person Eric Postpischil    schedule 30.01.2018
comment
@EricPostpischil Меня интересует использование только пяти цифр в моих вычислениях, чтобы проверить, как потеря цифр проявляется как ошибка при выполнении арифметических операций.   -  person Natasha    schedule 30.01.2018
comment
@Natasha Если num = 5.123499E-05, вы хотите, чтобы результат был примерно 5.1234E-05f или 5.1235E-05?   -  person chux - Reinstate Monica    schedule 30.01.2018


Ответы (4)


I'd go:

printf("%.6f", num);

Или вы можете попробовать использовать snprintf() из stdlib.h:

float num = 4.23654897E-05;  char output[50];

snprintf(output, 50, "%f", num);

printf("%s", output);
person Krzysztof Bielak    schedule 30.01.2018
comment
Это даже дает результат, как то, что OP упомянул в вопросе? - person Gaurav Pathak; 30.01.2018
comment
ну, если вы объедините первый метод для уменьшения чисел после комы и второй для чтения последних четырех символов - это сработает. Не самый простой способ, но эффективный - person Krzysztof Bielak; 30.01.2018

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

double l = pow(10, floor(log10(x)) - 4);
double y = l * floor(x / l);

Это полезно только для изучения арифметики с плавающей запятой. Точный математический результат, как правило, невозможно точно представить, потому что двоичная с плавающей запятой не может точно представить большинство десятичных значений. Кроме того, в операциях pow, / и * могут возникать ошибки округления, из-за которых результат может немного отличаться от истинного математического результата округления x до пяти значащих цифр. Кроме того, плохая реализация log10 или pow может привести к тому, что результат будет отличаться от истинного математического результата.

person Eric Postpischil    schedule 30.01.2018

Результат ожидаемый. Умножение на 10000 дает 0.423.., ближайшее к нему целое число 0. Таким образом, результат равен 0. Округление может быть выполнено с использованием спецификатора формата %f для печати результата с точностью до определенных десятичных знаков после десятичной точки.

Если вы проверите возвращаемое значение floorf, вы увидите, что оно возвращает If no errors occur, the largest integer value not greater than arg, that is ⌊arg⌋, is returned., где arg - переданный аргумент.

Без использования floatf вы можете использовать описатель формата %e или (%E) для его соответствующей печати.

printf("%.4E",num);

который выводит:

4.2365E-05

После комментария Дэвида:

Ваш образ действий правильный, но полученное вами число неверно. Дело в том, что 4.2365E-05 это 0.00004235.... Если теперь умножить его на 10000, получится 0.42365... Вы сказали, что я хочу, чтобы выражение было представлено в этой форме. floorf в этом случае возвращает float. Сохраните его в переменной, и все будет хорошо. Округленное значение будет в этой переменной. Но вы увидите, что округленное значение будет 0. Это то, что у вас есть.

float rounded_down = floorf(num * 10000) / 10000;

Это будет содержать правильное значение, округленное до 4 цифр после . (не в экспоненциальной нотации с E или e). Не путайте значение со спецификатором формата, используемым для его представления.

Что вам нужно сделать, чтобы получить желаемый результат, так это переместить десятичные знаки вправо. Для этого умножьте на большее число. (1e7 или 1e8 или как хотите).

person user2736738    schedule 30.01.2018
comment
Этот ответ объясняет, почему результат верен с учетом выходных данных. Поправьте меня, если я ошибаюсь. - person user2736738; 30.01.2018
comment
Хм. OP говорит: Я хотел бы использовать для вычислений первые пять цифр числа. Мне кажется, цель состоит в том, чтобы округлить значение до пяти значащих мест для использования в вычислениях, а не просто напечатать пять разрядов. Однако OP мог бы быть более ясным по этому поводу. - person ad absurdum; 30.01.2018
comment
@DavidBowling .: Тогда почему OP беспокоится о представлении или печати? Вопрос по сути неоднозначный. Теперь, если я изменю ответ, возникнет мысль, что на меня повлияли другие ответы. Это нежелательно. - person user2736738; 30.01.2018
comment
Я согласен, что вопрос можно было бы сформулировать лучше, но не думаю, что он неоднозначен. OP четко заявляет: я хотел бы использовать первые пять цифр числа для вычисления. Далее: я хочу использовать 4.2365E-05. Не похоже, что OP беспокоится о печатном представлении, за исключением того, что оно не такое, как ожидалось, когда проверяется значение rounded_down. - person ad absurdum; 30.01.2018
comment
@DavidBowling: Отредактировано. - person user2736738; 30.01.2018
comment
@DavidBowling: Есть ли у вас какие-либо отзывы относительно DV? Или какие-либо отзывы в целом? - person user2736738; 30.01.2018
comment
Обратная связь: из истории редактирования я вижу, что сначала на вопрос OP не был дан ответ, но вместо этого вы сказали, что результат был ожидаемым, учитывая код OP. Это очевидно, а не ответ. В его текущей форме ответ кажется неверным: ..._ 1_ Это будет содержать правильное значение, округленное до 4 цифр после .. неверно, или, по крайней мере, непонятно. Вместо умножения на 10000 OP необходимо умножить на 10000 * 10e5, чтобы получить желаемые 4 десятичных знака. Также обратите внимание, что окончательное значение было усечено, а не округлено. - person ad absurdum; 30.01.2018
comment
Думаю, вы понимаете, что вам нужно делать ... предполагает, что, возможно, вы понимаете, как исправить код OP, но, учитывая предыдущее неверное утверждение, общий ответ кажется неясным. Любой из них мог быть причиной голосования против. - person ad absurdum; 30.01.2018
comment
Незначительное: обратите внимание, что 4.2365E-005 не является ожидаемым результатом для C. Ожидается 4.2365E-05 (2-значная экспонента). Возможно, вы используете несовместимый или ранний компилятор C? - person chux - Reinstate Monica; 30.01.2018
comment
@DavidBowling .: Это способ сказать ... Я не догадываюсь, ответ, я говорю, что OP теперь должно быть ясно. Во-вторых, относительно первого пункта .._ 1_ Я сказал, что 4 знака после десятичной точки, если он написан в развернутой форме, а не с показателем степени, как вы могли подумать. Во всяком случае я редактировал ... - person user2736738; 30.01.2018
comment
@DavidBowling: Спасибо. - person user2736738; 30.01.2018
comment
@chux .: Кстати, спасибо. В моем предыдущем комментарии говорилось, что я запускал его в старом компиляторе ... теперь я запустил его в последнем компиляторе gcc - он дал ожидаемый результат. - person user2736738; 30.01.2018

Я хотел бы использовать для вычислений первые пять цифр числа.

Обычно числа с плавающей запятой кодируются с использованием двоичного кода, и OP хочет использовать 5 значащих десятичных цифр. Это проблематично, поскольку числа типа 4.23654897E-05 и 4.2365E-05 не точно представлены как float/double. Лучшее, что мы можем сделать, это подойти поближе.

Подход floor*() имеет проблемы с 1) отрицательными числами (следовало использовать trunc()) и 2) значениями около x.99995, которые во время округления могут изменить количество цифр. Я настоятельно рекомендую не использовать его здесь, так как такие решения, использующие его, терпят неудачу во многих угловых случаях.

Подход *10000 * power10, round, /(10000 * power10) страдает 1) power10 вычислением (в данном случае 1e5) 2) ошибками округления в кратном, 3) потенциалом переполнения. Необходимый power10 может быть неточным. * ошибки появляются в тех случаях, когда продукт близок к xxxxx.5. Часто этот промежуточный расчет выполняется с использованием более широкой double математики, поэтому угловые случаи редки. Плохое округление с использованием (some_int_type), имеющего ограниченный диапазон и являющегося усечением вместо лучших round() или rint().

Подход, который приближается к цели OP: вывести до 5 значащих цифр с помощью %e и преобразовать обратно. Не очень эффективен, но хорошо справляется со всеми случаями.

int main(void) {
  float num = 4.23654897E-05f;

  //         sign   d   .   dddd   e   sign   expo + \0
  #define N (1    + 1 + 1 + 4    + 1 + 1    + 4    + 1)
  char buf[N*2];  // Use a generous buffer - I like 2x what I think is needed.

  // OP wants 5 significant digits so print 4 digits after the decimal point.
  sprintf(buf, "%.4e", num);

  float rounded = (float) atof(buf);
  printf("%.5e %s\n", rounded, buf);
}

Вывод

4.23650e-05 4.2365e-05

Почему 5 в %.5e: Обычно float выводит до 6 значащих десятичных цифр, как и ожидалось (исследование FLT_DIG), поэтому печатаются 5 цифр после десятичной точки. точное значение rounded в этом случае было около 4.236500171...e-05, так как 4.2365e-05 не может быть точно представлен как float.

person chux - Reinstate Monica    schedule 30.01.2018
comment
Не могли бы вы объяснить следующие строки кода '#define N (1 + 1 + 1 + 4 + 1 + 1 + 4 + 1) char buf [N * 2]; ' - person Natasha; 31.01.2018
comment
1 + 1 + 1 + 4 + 1 + 1 + 4 + 1 относится к комментарию над ним - для печати значения с плавающей запятой с 5 значащими цифрами может потребоваться буфер длиной до 14 символов. Сказав это, мало причин для чрезмерной уверенности в потенциальных потребностях в буфере - поэтому я удваиваю его (2x). - person chux - Reinstate Monica; 31.01.2018
comment
Извините, я не понял этого , может потребоваться буфер длиной до 14 символов для печати значения с плавающей запятой с 5 значащими цифрами. Также объясните разницу между float num = 4.23654897E-05f; и число с плавающей запятой = 4.23654897E-05 - person Natasha; 31.01.2018
comment
@ Наташа Вам знакома разница между float и double? Примените это различие здесь: 4.23654897E-05f - это float константа. 4.23654897E-05 - это double константа. - person chux - Reinstate Monica; 31.01.2018
comment
Было бы неправильно, если бы мы определяли как float num = 4.23654897E-05 и double num = 4.23654897E-05 - person Natasha; 31.01.2018
comment
@Natasha Нет ничего плохого в double num=4.23654897E-05;, если изменение типа num вас устраивает. - person chux - Reinstate Monica; 31.01.2018
comment
Я запутался. Я понимаю, что использование числа с плавающей запятой = 4.23654897E-05 позволит компилятору назначить четыре байта, двойное число = 4.23654897E-05 назначит 8 байтов. Когда вы говорите, что 4.23654897E-05f является константа с плавающей запятой. 4.23654897E-05 - двойная константа. Вы имеете в виду, что когда мы используем число с плавающей запятой = 4.23654897E-05, выделяется 8 байтов, а для числа с плавающей запятой = 4.23654897E-05f выделяется 4 байта? - person Natasha; 31.01.2018
comment
@Natasha С float num = 4.23654897E-05; компиляторы конвертируют текст 4.23654897E-05 в 8-байтовый double. Поскольку этот double назначается 4-байтовому float, этот double преобразуется в float и назначается num. С float num = 4.23654897E-05f; компиляторы преобразуют текст 4.23654897E-05f в 4-байтовый float и присваивают его num. Оба, безусловно, будут выдавать один и тот же код. Первый может также вызвать предупреждение, например предупреждение: преобразование в 'float' изменяет постоянное значение 'double', поскольку 4.23654897E-05 и 4.23654897E-05f имеют почти одинаковые, но разные значения. 2-го не будет. - person chux - Reinstate Monica; 31.01.2018
comment
@Natasha Для упрощения: 0.1 != 0.1f как 0.1 не может быть представлен точно как double или float. Учитывая, что float/double имеют разную точность, используемые приблизительные значения различаются. - person chux - Reinstate Monica; 31.01.2018
comment
float numf = 4.23654897E-05f; число с плавающей запятой = 4.23654897E-05; печать этих значений до 15 знаков после запятой на компиляторе gcc дает в качестве вывода 4.236548920744099e-05 4.236548920744099e-05. Я тоже не получил никакого предупреждения - person Natasha; 01.02.2018
comment
@Natasha Разницы в выводе не ожидается, поскольку оба float имеют одинаковое значение. 4.23654897E-05f и 4.23654897E-05 - разные значения. Компиляция не получила предупреждения, потому что не все ваши предупреждения включены: -Wfloat-conversion in gcc .. (или слабый компилятор). Обратите внимание: то, что вы называете 15 десятичными знаками, - это 16 значащих десятичных цифр. - person chux - Reinstate Monica; 01.02.2018