Не надо ничего округлять, особенно если эти данные используются в дальнейших вычислениях, у дробных типов и так хватает проблем с представлением десятичных дробных чисел. Decimal
проблем с представлением не имеет, он вроде как десятичный, но у него сильно ограничен диапазон значений (даже в сравнении с float
) и он не используется математическими функциями, поэтому вместо округлений, появляются постоянные приведения.
Для вывода просто используйте форматированный вывод, например так:
double val = 01234.56789;
textBox1.Text = val.ToString("F2");//1234,57
Будет выведена целая часть целиком и ровно 2 знака после запятой. Осторожно с большими степенями, 1e100.ToString("F2")
, например, сформирует строку длиной 103 знака (100 на целую часть + разделитель дробной части + 2 знака дробной части). В случае больших степеней лучше подобрать другой формат, подробное описание возможных форматов тут и тут. Формат указывается обычной строкой, поэтому вы можете формировать ее динамически, например меняя точность вывода по необходимости.
Для ввода чисел, см. соседний ответ.
NSluggard 0 / 0 / 0 Регистрация: 24.12.2014 Сообщений: 9 |
||||
1 |
||||
24.12.2014, 18:26. Показов 17844. Ответов 28 Метки нет (Все метки)
Друзья, помогите допилить обработчик, мне нужно ограничить ввод в textbox до двух знаков после запятой, как сюда запихнуть сравнение типа «если длина после запятой больше», голову уже сломал
__________________
0 |
zna926 546 / 477 / 315 Регистрация: 24.09.2013 Сообщений: 3,345 Записей в блоге: 1 |
||||
25.12.2014, 15:56 |
2 |
|||
1 |
0 / 0 / 0 Регистрация: 24.12.2014 Сообщений: 9 |
|
25.12.2014, 16:29 [ТС] |
3 |
Спасибо, у меня сейчас подобная конструкция используется.
0 |
tezaurismosis Администратор 9356 / 4638 / 755 Регистрация: 17.04.2012 Сообщений: 9,490 Записей в блоге: 14 |
||||
25.12.2014, 17:04 |
4 |
|||
1 |
0 / 0 / 0 Регистрация: 24.12.2014 Сообщений: 9 |
|
25.12.2014, 18:04 [ТС] |
5 |
tezaurismosis, спасибо за наводку! к сожалению, тоже не вариант, нужно условие писать на проверку точки в textBox, после заполнении двух знаков после ноля, конечное число не редактируется
0 |
Just_Neshta 4 / 4 / 0 Регистрация: 10.11.2014 Сообщений: 27 |
||||
25.12.2014, 21:02 |
6 |
|||
Знаю, что не к месту, но вот друг подсказал: попробуй используй регулярные выражения.
Для проверки IP.
0 |
NSluggard 0 / 0 / 0 Регистрация: 24.12.2014 Сообщений: 9 |
||||
25.12.2014, 21:53 [ТС] |
7 |
|||
Just_Neshta, спасибо за совет (почему-то плюсик/спасибо поставить вам не получается?!), регулярные выражения пробовал,
курсор съезжает, textBox1.Select(textBox1.Text.Length, 0); возвращает курсор, но в конец дробной части, при вводе числа, сразу дробная часть появляется, к тому же, почему-то при вводе 18 символа в основной части вместо вводимых чисел подставляются ноли. В общем, решение простое в одну строчку (если закрыть глаза на проверку пустого значения в боксе), но похоже не рабочее или у меня фантазия кончилась.
0 |
2146 / 1283 / 516 Регистрация: 04.03.2014 Сообщений: 4,092 |
|
26.12.2014, 14:02 |
8 |
есть готовый контрол numericUpDown, где количество знаков после запятой выставляется .
0 |
BozKurt 306 / 283 / 102 Регистрация: 06.05.2014 Сообщений: 861 |
||||
26.12.2014, 14:37 |
9 |
|||
РешениеНе по теме: Metall_Version, ну вот хочется человеку, почему бы и нет? По делу…
В качестве разделителя можно использовать как запятую, так и точку. Проверки на то, что это именно дробное число, а не что-то другое (например: blabla123,456blabla) — нет. Но это легко решается при помощи Double.TryParse (с опять же таки, ограничением точности в 15 знаков).
1 |
0 / 0 / 0 Регистрация: 24.12.2014 Сообщений: 9 |
|
26.12.2014, 14:58 [ТС] |
10 |
BozKurt, спасибо за совет! но почему-то у меня ничего не происходит, пошел курить маску «(d+(,|.)d{2})d+», @»$1″
0 |
306 / 283 / 102 Регистрация: 06.05.2014 Сообщений: 861 |
|
26.12.2014, 15:01 |
11 |
у меня ничего не происходит TextBox — точно под индексом «один»? Просто я на форму только один бросал, у тебя по всей видимости textBox6.
0 |
NSluggard 0 / 0 / 0 Регистрация: 24.12.2014 Сообщений: 9 |
||||
26.12.2014, 15:10 [ТС] |
12 |
|||
Metall_Version, numericUpDown — количество знаков там конечно выставляется, но вписать самостоятельно можно любое количество Добавлено через 2 минуты
0 |
306 / 283 / 102 Регистрация: 06.05.2014 Сообщений: 861 |
|
26.12.2014, 15:12 |
13 |
И не работает?
1 |
0 / 0 / 0 Регистрация: 24.12.2014 Сообщений: 9 |
|
26.12.2014, 15:13 [ТС] |
14 |
BozKurt, разобрался — заработало!!! СПАСИБО!!!
0 |
BozKurt 306 / 283 / 102 Регистрация: 06.05.2014 Сообщений: 861 |
||||
26.12.2014, 16:50 |
15 |
|||
РешениеNSluggard, разделение на разряды с помощью регулярок, да ещё и в одной — вероятно невозможная задача.
Универсальное событие. Вешаешь на TextChanged нужных тебе TextBox‘ов и они все будет вести себя одинаково (ничего заменять не нужно).
2 |
0 / 0 / 0 Регистрация: 24.12.2014 Сообщений: 9 |
|
26.12.2014, 17:55 [ТС] |
16 |
BozKurt, ничего себе, wow — вот это круто!
0 |
306 / 283 / 102 Регистрация: 06.05.2014 Сообщений: 861 |
|
26.12.2014, 18:23 |
17 |
В любое событие Windows Form первым аргументом передаётся сам объект который это самое событие вызвал, он приводится к типу object. Чтобы не задумываться о том, как именно называется объект, который вызвал событие, можно привести первый аргумент (sender) к нужному тебе типу, в данном случае это TextBox и я точно знаю, что ничего другое это событие не вызовет, т.е. если это событие подключить к любому другому контролу отличному от TextBox, даже к RichTextBox‘у, его выполнение вызовет исключение уже в первой строке. Поэтому ничего «неявного» в приведённом коде нет, object приводится к конкретному типу, который программист точно должен знать.
1 |
0 / 0 / 0 Регистрация: 24.12.2014 Сообщений: 9 |
|
26.12.2014, 19:59 [ТС] |
18 |
Огромное спасибо за разъяснение!
0 |
BozKurt |
26.12.2014, 20:03
|
0 |
pirat2k 11 / 11 / 8 Регистрация: 18.09.2012 Сообщений: 514 |
||||
25.09.2015, 11:35 |
20 |
|||
вот мое решение. тут не только ввод после запятой
3 |
IT_Exp Эксперт 87844 / 49110 / 22898 Регистрация: 17.06.2006 Сообщений: 92,604 |
25.09.2015, 11:35 |
20 |
This i what I am trying to achieve:
If a double has more than 3 decimal places, I want to truncate any decimal places beyond the third. (do not round.)
Eg.: 12.878999 -> 12.878
If a double has less than 3 decimals, leave unchanged
Eg.: 125 -> 125
89.24 -> 89.24
I came across this command:
double example = 12.34567;
double output = Math.Round(example, 3);
But I do not want to round. According to the command posted above,
12.34567 -> 12.346
I want to truncate the value so that it becomes: 12.345
asked Sep 28, 2010 at 15:09
1
Doubles don’t have decimal places — they’re not based on decimal digits to start with. You could get «the closest double to the current value when truncated to three decimal digits», but it still wouldn’t be exactly the same. You’d be better off using decimal
.
Having said that, if it’s only the way that rounding happens that’s a problem, you can use Math.Truncate(value * 1000) / 1000;
which may do what you want. (You don’t want rounding at all, by the sounds of it.) It’s still potentially «dodgy» though, as the result still won’t really just have three decimal places. If you did the same thing with a decimal value, however, it would work:
decimal m = 12.878999m;
m = Math.Truncate(m * 1000m) / 1000m;
Console.WriteLine(m); // 12.878
EDIT: As LBushkin pointed out, you should be clear between truncating for display purposes (which can usually be done in a format specifier) and truncating for further calculations (in which case the above should work).
answered Sep 28, 2010 at 15:12
Jon SkeetJon Skeet
1.4m851 gold badges9044 silver badges9133 bronze badges
10
I can’t think of a reason to explicitly lose precision outside of display purposes. In that case, simply use string formatting.
double example = 12.34567;
Console.Out.WriteLine(example.ToString("#.000"));
answered Sep 28, 2010 at 15:20
Chris MartinChris Martin
1,85116 silver badges17 bronze badges
3
double example = 3.1416789645;
double output = Convert.ToDouble(example.ToString("N3"));
answered Feb 5, 2014 at 20:31
Merin NakarmiMerin Nakarmi
3,0783 gold badges31 silver badges40 bronze badges
1
Multiply by 1000 then use Truncate then divide by 1000.
answered Sep 28, 2010 at 15:12
JoshJosh
67.3k14 gold badges140 silver badges154 bronze badges
1
If your purpose in truncating the digits is for display reasons, then you just just use an appropriate formatting when you convert the double to a string.
Methods like String.Format()
and Console.WriteLine()
(and others) allow you to limit the number of digits of precision a value is formatted with.
Attempting to «truncate» floating point numbers is ill advised — floating point numbers don’t have a precise decimal representation in many cases. Applying an approach like scaling the number up, truncating it, and then scaling it down could easily change the value to something quite different from what you’d expected for the «truncated» value.
If you need precise decimal representations of a number you should be using decimal
rather than double
or float
.
answered Sep 28, 2010 at 15:17
LBushkinLBushkin
128k32 gold badges213 silver badges261 bronze badges
0
You can use:
double example = 12.34567;
double output = ( (double) ( (int) (example * 1000.0) ) ) / 1000.0 ;
answered Sep 28, 2010 at 15:15
Pablo Santa CruzPablo Santa Cruz
174k32 gold badges239 silver badges291 bronze badges
1
Good answers above- if you’re looking for something reusable here is the code. Note that you might want to check the decimal places value, and this may overflow.
public static decimal TruncateToDecimalPlace(this decimal numberToTruncate, int decimalPlaces)
{
decimal power = (decimal)(Math.Pow(10.0, (double)decimalPlaces));
return Math.Truncate((power * numberToTruncate)) / power;
}
answered Sep 28, 2010 at 15:26
Mike M.Mike M.
12.1k1 gold badge22 silver badges28 bronze badges
In C lang:
double truncKeepDecimalPlaces(double value, int numDecimals)
{
int x = pow(10, numDecimals);
return (double)trunc(value * x) / x;
}
answered Mar 6, 2014 at 12:11
This i what I am trying to achieve:
If a double has more than 3 decimal places, I want to truncate any decimal places beyond the third. (do not round.)
Eg.: 12.878999 -> 12.878
If a double has less than 3 decimals, leave unchanged
Eg.: 125 -> 125
89.24 -> 89.24
I came across this command:
double example = 12.34567;
double output = Math.Round(example, 3);
But I do not want to round. According to the command posted above,
12.34567 -> 12.346
I want to truncate the value so that it becomes: 12.345
asked Sep 28, 2010 at 15:09
1
Doubles don’t have decimal places — they’re not based on decimal digits to start with. You could get «the closest double to the current value when truncated to three decimal digits», but it still wouldn’t be exactly the same. You’d be better off using decimal
.
Having said that, if it’s only the way that rounding happens that’s a problem, you can use Math.Truncate(value * 1000) / 1000;
which may do what you want. (You don’t want rounding at all, by the sounds of it.) It’s still potentially «dodgy» though, as the result still won’t really just have three decimal places. If you did the same thing with a decimal value, however, it would work:
decimal m = 12.878999m;
m = Math.Truncate(m * 1000m) / 1000m;
Console.WriteLine(m); // 12.878
EDIT: As LBushkin pointed out, you should be clear between truncating for display purposes (which can usually be done in a format specifier) and truncating for further calculations (in which case the above should work).
answered Sep 28, 2010 at 15:12
Jon SkeetJon Skeet
1.4m851 gold badges9044 silver badges9133 bronze badges
10
I can’t think of a reason to explicitly lose precision outside of display purposes. In that case, simply use string formatting.
double example = 12.34567;
Console.Out.WriteLine(example.ToString("#.000"));
answered Sep 28, 2010 at 15:20
Chris MartinChris Martin
1,85116 silver badges17 bronze badges
3
double example = 3.1416789645;
double output = Convert.ToDouble(example.ToString("N3"));
answered Feb 5, 2014 at 20:31
Merin NakarmiMerin Nakarmi
3,0783 gold badges31 silver badges40 bronze badges
1
Multiply by 1000 then use Truncate then divide by 1000.
answered Sep 28, 2010 at 15:12
JoshJosh
67.3k14 gold badges140 silver badges154 bronze badges
1
If your purpose in truncating the digits is for display reasons, then you just just use an appropriate formatting when you convert the double to a string.
Methods like String.Format()
and Console.WriteLine()
(and others) allow you to limit the number of digits of precision a value is formatted with.
Attempting to «truncate» floating point numbers is ill advised — floating point numbers don’t have a precise decimal representation in many cases. Applying an approach like scaling the number up, truncating it, and then scaling it down could easily change the value to something quite different from what you’d expected for the «truncated» value.
If you need precise decimal representations of a number you should be using decimal
rather than double
or float
.
answered Sep 28, 2010 at 15:17
LBushkinLBushkin
128k32 gold badges213 silver badges261 bronze badges
0
You can use:
double example = 12.34567;
double output = ( (double) ( (int) (example * 1000.0) ) ) / 1000.0 ;
answered Sep 28, 2010 at 15:15
Pablo Santa CruzPablo Santa Cruz
174k32 gold badges239 silver badges291 bronze badges
1
Good answers above- if you’re looking for something reusable here is the code. Note that you might want to check the decimal places value, and this may overflow.
public static decimal TruncateToDecimalPlace(this decimal numberToTruncate, int decimalPlaces)
{
decimal power = (decimal)(Math.Pow(10.0, (double)decimalPlaces));
return Math.Truncate((power * numberToTruncate)) / power;
}
answered Sep 28, 2010 at 15:26
Mike M.Mike M.
12.1k1 gold badge22 silver badges28 bronze badges
In C lang:
double truncKeepDecimalPlaces(double value, int numDecimals)
{
int x = pow(10, numDecimals);
return (double)trunc(value * x) / x;
}
answered Mar 6, 2014 at 12:11
@NovichekTyrnira
Начинающий гений C# индустрии
Как мне вывести b И c не округляя их?
В коде ниже переменные b и с округляются
using System;
using System.IO;
class Program
{
static void Main()
{
double x = Convert.ToDouble(Console.ReadLine());
double b = Math.PI * Math.Sqrt(x);
double c = 2 * Math.PI * x;
Console.WriteLine("{0: 0.000}",b);
Console.WriteLine("{0: 0.000}",c);
}
}
-
Вопрос заданболее двух лет назад
-
18813 просмотров
Судя по всему, стандартными средствами форматирования никак
Можно так
Console.WriteLine("{0:f4}", Math.Truncate(10000 * Math.PI) / 10000);
Можно так
Console.WriteLine(TruncateWithPrecision(Math.PI , 4));
static string TruncateWithPrecision(double value, int precision)
{
if (precision < 0) throw new ArgumentOutOfRangeException(nameof(precision));
var prepared = string.Format($"{{0:f{precision + 1}}}", value);
return prepared.Substring(0, prepared.Length - (precision == 0 ? 2 : 1));
}
Пригласить эксперта
Здрасьте, я ещё совсем новичок в сфере программирования, но хотелось бы оставить свой ответ на случай, если у такого же неопытного юнца, как я, возникнет какая-то схожая проблема
Перед выводом, после всех исчислений, можно воспользоваться таким методом:
double c = Math.Round(c, 2);
-
Показать ещё
Загружается…
06 февр. 2023, в 12:36
20000 руб./за проект
06 февр. 2023, в 12:35
7000 руб./за проект
06 февр. 2023, в 12:30
8000 руб./за проект
Минуточку внимания
Always use the printf
family of functions for this. Even if you want to get the value as a float, you’re best off using snprintf
to get the rounded value as a string and then parsing it back with atof
:
#include <math.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
double dround(double val, int dp) {
int charsNeeded = 1 + snprintf(NULL, 0, "%.*f", dp, val);
char *buffer = malloc(charsNeeded);
snprintf(buffer, charsNeeded, "%.*f", dp, val);
double result = atof(buffer);
free(buffer);
return result;
}
I say this because the approach shown by the currently top-voted answer and several others here —
multiplying by 100, rounding to the nearest integer, and then dividing by 100 again — is flawed in two ways:
- For some values, it will round in the wrong direction because the multiplication by 100 changes the decimal digit determining the rounding direction from a 4 to a 5 or vice versa, due to the imprecision of floating point numbers
- For some values, multiplying and then dividing by 100 doesn’t round-trip, meaning that even if no rounding takes place the end result will be wrong
To illustrate the first kind of error — the rounding direction sometimes being wrong — try running this program:
int main(void) {
// This number is EXACTLY representable as a double
double x = 0.01499999999999999944488848768742172978818416595458984375;
printf("x: %.50fn", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.50fn", res1);
printf("Rounded with round, then divided: %.50fn", res2);
}
You’ll see this output:
x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
Note that the value we started with was less than 0.015, and so the mathematically correct answer when rounding it to 2 decimal places is 0.01. Of course, 0.01 is not exactly representable as a double, but we expect our result to be the double nearest to 0.01. Using snprintf
gives us that result, but using round(100 * x) / 100
gives us 0.02, which is wrong. Why? Because 100 * x
gives us exactly 1.5 as the result. Multiplying by 100 thus changes the correct direction to round in.
To illustrate the second kind of error — the result sometimes being wrong due to * 100
and / 100
not truly being inverses of each other — we can do a similar exercise with a very big number:
int main(void) {
double x = 8631192423766613.0;
printf("x: %.1fn", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.1fn", res1);
printf("Rounded with round, then divided: %.1fn", res2);
}
Our number now doesn’t even have a fractional part; it’s an integer value, just stored with type double
. So the result after rounding it should be the same number we started with, right?
If you run the program above, you’ll see:
x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0
Oops. Our snprintf
method returns the right result again, but the multiply-then-round-then-divide approach fails. That’s because the mathematically correct value of 8631192423766613.0 * 100
, 863119242376661300.0
, is not exactly representable as a double; the closest value is 863119242376661248.0
. When you divide that back by 100, you get 8631192423766612.0
— a different number to the one you started with.
Hopefully that’s a sufficient demonstration that using roundf
for rounding to a number of decimal places is broken, and that you should use snprintf
instead. If that feels like a horrible hack to you, perhaps you’ll be reassured by the knowledge that it’s basically what CPython does.
Always use the printf
family of functions for this. Even if you want to get the value as a float, you’re best off using snprintf
to get the rounded value as a string and then parsing it back with atof
:
#include <math.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
double dround(double val, int dp) {
int charsNeeded = 1 + snprintf(NULL, 0, "%.*f", dp, val);
char *buffer = malloc(charsNeeded);
snprintf(buffer, charsNeeded, "%.*f", dp, val);
double result = atof(buffer);
free(buffer);
return result;
}
I say this because the approach shown by the currently top-voted answer and several others here —
multiplying by 100, rounding to the nearest integer, and then dividing by 100 again — is flawed in two ways:
- For some values, it will round in the wrong direction because the multiplication by 100 changes the decimal digit determining the rounding direction from a 4 to a 5 or vice versa, due to the imprecision of floating point numbers
- For some values, multiplying and then dividing by 100 doesn’t round-trip, meaning that even if no rounding takes place the end result will be wrong
To illustrate the first kind of error — the rounding direction sometimes being wrong — try running this program:
int main(void) {
// This number is EXACTLY representable as a double
double x = 0.01499999999999999944488848768742172978818416595458984375;
printf("x: %.50fn", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.50fn", res1);
printf("Rounded with round, then divided: %.50fn", res2);
}
You’ll see this output:
x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
Note that the value we started with was less than 0.015, and so the mathematically correct answer when rounding it to 2 decimal places is 0.01. Of course, 0.01 is not exactly representable as a double, but we expect our result to be the double nearest to 0.01. Using snprintf
gives us that result, but using round(100 * x) / 100
gives us 0.02, which is wrong. Why? Because 100 * x
gives us exactly 1.5 as the result. Multiplying by 100 thus changes the correct direction to round in.
To illustrate the second kind of error — the result sometimes being wrong due to * 100
and / 100
not truly being inverses of each other — we can do a similar exercise with a very big number:
int main(void) {
double x = 8631192423766613.0;
printf("x: %.1fn", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.1fn", res1);
printf("Rounded with round, then divided: %.1fn", res2);
}
Our number now doesn’t even have a fractional part; it’s an integer value, just stored with type double
. So the result after rounding it should be the same number we started with, right?
If you run the program above, you’ll see:
x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0
Oops. Our snprintf
method returns the right result again, but the multiply-then-round-then-divide approach fails. That’s because the mathematically correct value of 8631192423766613.0 * 100
, 863119242376661300.0
, is not exactly representable as a double; the closest value is 863119242376661248.0
. When you divide that back by 100, you get 8631192423766612.0
— a different number to the one you started with.
Hopefully that’s a sufficient demonstration that using roundf
for rounding to a number of decimal places is broken, and that you should use snprintf
instead. If that feels like a horrible hack to you, perhaps you’ll be reassured by the knowledge that it’s basically what CPython does.
Иногда бывает нужно ограничить ввод до двух знаков после запятой.
Следующая функция возвращает true
, если входное значения является действительным, в противном случае она возвращает значение false
:
class ValidAmount
{
const int KEYCODE_FOR_DOT = 190;
public static bool IsValidMoneyInput(String previousInput, String key, int keyCode)
{
if (!String.IsNullOrWhiteSpace(previousInput))
{
if (previousInput.Contains("."))
{
if (keyCode == KEYCODE_FOR_DOT)
{
return false;
}
else
{
String[] strings = previousInput.Split('.');
if (strings[1].Length > 1)
{
return false;
}
}
}
}
return true;
}
}
Для использования данной функции, вам нужно навесить обработчик на событие Key_Down
для текстового поля (TextBox) и поместить в него следующий код:
private void Amounnt_OnKeyDown(object sender, KeyEventArgs e)
{
e.Handled = !ValidAmount.IsValidMoneyInput(AmountToSend.Text, e.Key.ToString(), e.PlatformKeyCode);
}
Оригинал
Теги: перевод, c#
Редактировать