Як перетворити число з плаваючою комою в ціле число?


47

Це правильний спосіб зробити плаваюче на ціле перетворення в bash? Чи є якийсь інший метод?

flotToint() {
    printf "%.0f\n" "$@"
}

5
Що таке "правильно"?
Джозеф Р.

Я просто хочу переконатися .. це правильно чи є краще, ніж це?
Рахул Патіль

2
%.0fбуде округлятися вгору або вниз. Це те, чого ти хочеш? Можна використовувати printf "%d\n" "$@" 2>/dev/nullдля подрібнення фракції.
ott--

Відповіді:


69

баш

В bash, мабуть, це так добре, як це виходить. Тут використовується вбудована оболонка. Якщо вам потрібен результат у змінній, ви можете використовувати підстановку команд або bashспецифічну (хоча зараз це також підтримується zsh):

printf -v int %.0f "$float"

Ви можете зробити:

float=1.23
int=${float%.*}

Але це видалить дробову частину замість того, щоб дати вам найближче ціле число, і це не працюватиме для значень $floatтипу 1.2e9або, .12наприклад.

Також зверніть увагу на можливі обмеження через внутрішнє представлення плавців:

$ printf '%.0f\n' 1e50
100000000000000007629769841091887003294964970946560

Ви отримуєте ціле число, але є ймовірність, що ви не зможете використовувати це ціле число ніде.

Також, як зазначає @BinaryZebra, у кількох printfреалізаціях (bash, ksh93, yash, не GNU, zsh, dash) на нього впливає локаль (десятковий роздільник, який може бути .або ,).

Отже, якщо ваші плавці завжди виражаються періодом у вигляді десяткового роздільника, і ви хочете, щоб він розглядався як такий printfнезалежно від локальної локації користувача, що викликає ваш сценарій, вам потрібно буде виправити локаль на C:

LC_ALL=C printf '%.0f' "$float"

Завдяки цьому yashви також можете:

printf '%.0f' "$(($float))"

(Дивіться нижче).

POSIX

printf "%.0f\n" 1.1

не є POSIX, оскільки %fне потрібно підтримувати POSIX.

POSIXly, ви можете:

f2i() {
  awk 'BEGIN{for (i=1; i<ARGC;i++)
   printf "%.0f\n", ARGV[i]}' "$@"
}

На цю локаль не впливає (кома не може бути десятковою сепаратором, awkоскільки це вже спеціальний символ у синтаксисі (так print 1,2само, як print 1, 2передавати два аргументи print)

зш

У zsh(який підтримує арифметику з плаваючою комою (десятковий роздільник завжди є періодом)), у вас є rint()математична функція, щоб дати вам найближче ціле число як поплавок (як у C) і int()дати вам ціле число від поплавця (як в awk). Отже, ви можете зробити:

$ zmodload zsh/mathfunc
$ i=$((int(rint(1.234e2))))
$ echo $i
123

Або:

$ integer i=$((rint(5.678e2)))
$ echo $i
568

Однак зауважте, що хоча doubles може представляти дуже великі числа, цілі числа значно обмежені.

$ printf '%.0f\n' 1e123
999999999999999977709969731404129670057984297594921577392083322662491290889839886077866558841507631684757522070951350501376
$ echo $((int(1e123)))
-9223372036854775808

кш93

ksh93 була першою оболонкою Борна, яка підтримувала арифметику з плаваючою точкою. ksh93 оптимізує заміну команд, не використовуючи трубу або розкручуючи, коли команди є лише вбудованими командами. Тому

i=$(printf '%.0f' "$f")

не роздрібнюється. Або ще краще:

i=${ printf '%.0f' "$f"; }

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

Ви також можете зробити:

i=$((rint(f)))

Але будьте обережні:

$ echo "$((rint(1e18)))"
1000000000000000000
$ echo "$((rint(1e19)))"
1e+19

Ви також можете зробити:

integer i=$((rint(f)))

Але як для zsh:

$ integer i=1e18
$ echo "$i"
1000000000000000000
$ integer i=1e19
$ echo "$i"
-9223372036854775808

Остерігайтеся, щоб ksh93арифметика з плаваючою комою шанувала параметр десяткового роздільника в локалі (навіть якщо ,це інакше математичний оператор ( $((1,2))було б 6/5 у французькому / німецькому ... мові, і те саме $((1, 2)), що 2 в англійській мові) .

яш

yash також підтримує арифметику з плаваючою комою, але не має математичних функцій, як ksh93/ zshs rint(). Ви можете перетворити число в ціле число, хоча, наприклад, використовуючи двійковий або оператор (також працює в, zshале не в ksh93). Однак зауважте, що він урізає десяткову частину, вона не дає вам найближчого цілого числа:

$ echo "$((0.237e2 | 0))"
23
$ echo "$((1e19))"
-9223372036854775808

yash вшановує десятковий роздільник локалі на виході, але не для буквальних констант плаваючої точки в його арифметичних виразах, що може викликати сюрпризи:

$ LC_ALL=fr_FR.UTF-8 ./yash -c 'a=$((1e-2)); echo $(($a + 1))'
./yash: arithmetic: `,' is not a valid number or operator

Це добре тим, що ви можете використовувати константи з плаваючою точкою у своїх сценаріях, які використовують період, і не потрібно хвилюватися, що він перестане працювати в інших локалях, але все-таки матимете змогу працювати з числами, вираженими користувачем, поки як ви пам'ятаєте робити:

var=$((10.3)) # and not var=10.3
... "$((a + 0.1))" # and not "$(($a + 0.1))".

printf '%.0f\n' "$((10.3))" # and not printf '%.0f\n' 10.3

int=${float%.*}дуже добре працював у моєму скрипті bash (версія 3.2.57 (1) -release) на Mac (версія 10.13.4 (17E199)).
TelamonAegisthus

Спочатку формулювання не вдається в GNU bash 4.4.19, наприклад: `` `$ echo $ максимум 32 800 $ printf -v int% .0f" $ максимум "bash: printf: 32.800: недійсне число` `` `
Luís de Sousa

@ LuísdeSousa, напевно, у вашому мові є ,десятковий радіус. Дивіться розділ проLC_ALL=C
Стефан Шазелас

Зверніть увагу , що ця відповідь відповідає на інше питання: Як видалити цифри після коми не те , що було поставлено питання: що таке право спосіб зробити поплавець в ціле .
Ісаак

Чи слід застосовувати цей метод до плавців будь-якої кількості бітів? Чи має бути однаково для поплавця з 23 бітів або 128 біт мантіси?
Ісаак

21

bc - Довільна мова калькулятора точності

int (float) має виглядати так:

$ echo "$float/1" | bc 
1234

Для округлення краще скористайтеся цим:

$ echo "($float+0.5)/1" | bc 

Приклад:

$ float=1.49
$ echo "($float+0.5)/1" | bc 
1
$ float=1.50
$ echo "($float+0.5)/1" | bc 
2

4
Але float=-2; echo "($float+0.5)/1" | bcдає -1.
Стефан Шазелас

2
Це називається круглим у напрямку до + інф. Це один з чотирьох можливих способів обійти .

5

Попередня подана відповідь була майже вірною: "Ви можете зробити:

float=1.23
int=${float%.*}

Але це видалить дробову частину замість того, щоб дати вам найближче ціле число, і це не працює для значень $ float, як, наприклад, 1.2e9 або .12 .... "

Просто використовуйте ${float%%.*}.

echo ${float%%.*}
1

3

Дуже простий хакі

function float_to_int() { 
  echo $1 | cut -d. -f1    # or use -d, if decimals separator is ,
}

Вибірка зразка

$ float_to_int 32.333
32
$ float_to_int 32
32

Можна використовувати sed, але cutрішення простіше. Я вважаю за краще sedабо cutяк вони IMHO доступніші для вбудованих цілей, ніж awk(що все ще досить часто, busyboxніж через bc).
pevik

0

Пов'язані:

  1. як зробити плавати цілим числом у awk ,
  2. Як округлити числа з плаваючою комою в оболонці?

Відповідь @ Стефана Шазеласа awk :

float_to_integer_rounding_nearst() {
    awk 'BEGIN{for (i=1; i<ARGC;i++) printf "%.0f", ARGV[i]}' "$@";
}

float_to_integer_rounding_floor() {
    awk 'BEGIN{for (i=1; i<ARGC;i++) printf "%d", int( ARGV[i] )}' "$@";
}

на моєму розпаді, останній закруглюється до нуля, а не до мінус нескінченності (як "підлогу" можна вважати). Також перші тури вдвічі зменшуються до парних чисел. Я не впевнений, чи це щось відрізняється від того, що printfробить Баш , або якщо є якась інша причина, щоб віддати перевагу awk над вбудованим printf.
ilkkachu
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.