Как преобразовать float в int в C#, чтобы он выводил то же значение, что и float.ToString ()?

Вызов ToString() с неточными числами с плавающей запятой дает числа, которые человек мог бы ожидать (например, 4.999999 ... печатается как 5). Однако при приведении к типу int дробная часть отбрасывается, и результатом является целое число, уменьшенное на 1.

float num = 5f;
num *= 0.01f;
num *= 100f;
num.ToString().Dump(); // produces "5", as expected
((int)num).ToString().Dump(); // produces "4"

Как мне преобразовать float в int, чтобы получить удобное для человека значение, которое производит float.ToString()?

Я использую этот грязный прием:

int ToInt(float value) {
    return (int)(value + Math.Sign(value) * 0.00001);
}

..но, безусловно, должно быть более элегантное решение.


Обновлено: мне хорошо известны причины, по которым поплавки усекаются так, как они есть (4,999 ... до 4 и т.д.). Вопрос заключается в приведении к int при имитации поведения System.Single.ToString () по умолчанию.

Чтобы лучше проиллюстрировать поведение, которое я ищу:

-4.99f следует преобразовать в -4
4.01f следует преобразовать в 4
4.99f следует преобразовать в 4
4.999999f следует преобразовать в 4
4.9999993f следует преобразовать в 5

Это то же самое поведение, что и у ToString.

Попробуйте запустить это:

float almost5 = 4.9999993f;
Console.WriteLine(almost5); // "5"
Console.WriteLine((int)almost5); // "4"

Как насчет Math.Ceiling?

FindOutIslamNow 02.05.2018 12:53

Если число с плавающей запятой составляет 4,9375, что вы хотите напечатать: «4» или «5»? Или этого никогда не произойдет, потому что число с плавающей запятой никогда не будет настолько далеким от целого? Насколько далеко оно может быть от целого числа? В какой момент вы хотите, чтобы результат изменился с «5» на «4»?

Eric Postpischil 02.05.2018 12:55

@FindOutIslamNow: Как вы думаете, OP хочет, чтобы значение чуть больше 5 отображалось как «6»?

Eric Postpischil 02.05.2018 12:56

Я думаю, вы ищете (int) Math.Round(num)

Evk 02.05.2018 12:57

Используйте Math.Round --- (int)Math.Round(num)).ToString(). Функция Math.Round округляет значение с плавающей запятой до ближайшего целого числа и округляет средние значения до ближайшего четного числа.

user1672994 02.05.2018 12:57

Возможный дубликат Почему арифметика с плавающей запятой в C# неточна?

mjwills 02.05.2018 14:12

Я отредактировал вопрос и добавил примеры поведения, которое ищу. Вопрос не в неточности поплавков. Речь идет о том, чтобы изящно бороться с неточностями, как это делает Single.ToString. Я не ищу стандартный Math.Round ().

blade 02.05.2018 14:51

Итак, вы хотите округлить 4.xxxxxxxxxxxx до ближайшего 0,0000001. Как насчет 49.999993f, который должен быть 50,00000 или 49,99999?

chux - Reinstate Monica 02.05.2018 15:06
Console.WriteLine(49.999993f); печатает 49.99999. Я не уверен, где находится порог, и это действительно суть вопроса. Как ToString() выполняет преобразование? Обновлено: я пробовал Single.Epsilon, но это тоже не то.
blade 02.05.2018 15:11

@blade Преобразует число с плавающей запятой в строку, а затем в целое число, которое игнорирует дробь.

chux - Reinstate Monica 02.05.2018 15:49

@chux Это определенно сработает, но это тоже очень хакерское решение.

blade 02.05.2018 15:54

@blade OK, тогда назовите это ссылочной функцией и найдите способы ее улучшить. По крайней мере, ваша цель ясна и недвусмысленна. С almost5 = 4.9999993f вы испытываете двойное округление. 4.9999993f не является одним из 2 ^ 32 различных представимых float. Двумя ближайшими являются 4,9999990463 ... и 4,9999995232 ... с 4,9999995232 ... ближе (1-й раунд). Итак, вы действительно делаете almost5 = 4.9999995232f, а WriteLine(), по-видимому, округляет до 7 цифр, то есть 5. (2-й раунд)

chux - Reinstate Monica 02.05.2018 16:02

@chux Вы уверены, что округлите до 7 знаков после запятой? При печати двойного 4.9999999999 он не округляется. Или 7-значное округление применяется только к числам с плавающей запятой? Если да, то это могло быть решением, хотя я не уверен, насколько хорошо он справляется с неточностями: (int)Math.Round(value * 1000000) / 1000000?

blade 02.05.2018 16:40

@blade Я не уверен в деталях C#. Напомним, printing a double 4.9999999999 действительно пытается напечатать 4.99999999989999999173 ..., а не 4.9999999999. (int)Math.Round(...) не работает для float() вне диапазона int.

chux - Reinstate Monica 02.05.2018 16:52

@chux Это хороший момент. Спасибо за вводные данные, я думаю, что буду придерживаться своего исходного решения, установив значение допустимой погрешности равным 0,000001f. Это работает для каждого поплавка, который я бросаю в него. Разбор строки также будет работать, но он значительно медленнее.

blade 02.05.2018 17:06

Есть 2 ^ 32 различных float - вероятно, намного больше, чем «каждый поплавок, который я бросаю в него». Вы можете протестировать каждый float с вашим быстрым решением против медленного подхода «Разбор строки», чтобы проверить его правильность.

chux - Reinstate Monica 02.05.2018 18:11
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
16
746
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

0.05f * 100 не совсем 5 из-за округления с плавающей запятой (на самом деле это значение 4,999998 .... при выражении в виде числа с плавающей запятой

Ответ заключается в том, что в случае (int)(.05f * 100) вы берете значение с плавающей запятой 4.999998 и обрезаете его до целого числа, что дает 4.

Так что используйте Math.Round. Функция Math.Round округляет значение с плавающей запятой до ближайшего целого числа и округляет средние значения до ближайшего четного числа.

    float num = 5f;
    num *= 0.01f;
    num *= 100f;
    Console.WriteLine(num.ToString());
    Console.WriteLine(((int)Math.Round(num)).ToString());

Может быть, вы ищете это:

Convert.ToInt32 (с плавающей запятой)

источник

Похоже, это эквивалент Math.Round(), чего я не ищу. Я прояснил вопрос.

blade 02.05.2018 14:46
Ответ принят как подходящий

Пока что лучшим решением является решение вопроса.

int ToInt(float value) {
    return (int)(value + Math.Sign(value) * 0.000001f);
}

Это эффективно привязывает значение к ближайшему int, если разница достаточно мала (меньше, чем 0.000001). Однако эта функция отличается от поведения ToString и немного более терпима к неточностям.

Другое решение, предложенное @chux, - использовать ToString и проанализировать строку обратно. Использование Int32.Parse вызывает исключение, когда число имеет десятичную точку (или запятую), поэтому вам нужно сохранить только целую часть строки, и это может вызвать другие проблемы в зависимости от вашего CultureInfo по умолчанию.

Другие вопросы по теме