Я встретился с раздражающей проблемой в выводе числа с плавающей точкой. Когда я формат 11.545 с точностью 2 десятичных точек в Windows, который это производит "11.55", как я ожидал бы. Однако, когда я делаю то же на Linux, вывод "11.54"!
Я первоначально встретился с проблемой в Python, но дальнейшее расследование показало, что различие находится в базовой библиотеке времени выполнения C. (Архитектура является x86-x64 в обоих случаях.) Выполнение следующей строки C приводит к различным результатам в Windows и Linux, то же, как это делает в Python.
printf("%.2f", 11.545);
Для проливания большего количества света на это, я распечатал число к 20 десятичным разрядам ("%.20f"
):
Windows: 11.54500000000000000000
Linux: 11.54499999999999992895
Я знаю, что 11.545 не может быть сохранен точно как двоичное число. Таким образом, то, что, кажется, происходит, - то, что Linux производит число, это на самом деле снабжено самой лучшей точностью, в то время как Windows производит самое простое десятичное представление его, т.е. пытается предположить то, что, скорее всего, имел в виду пользователь.
Мой вопрос: там какой-либо (разумный) путь состоит в том, чтобы эмулировать поведение Linux в Windows?
(В то время как поведение Windows является, конечно, интуитивным, в моем случае я на самом деле должен сравнить вывод Windows-программы с той из программы Linux и Windows, каждый - единственный, которого я могу изменить. Между прочим, я пытался посмотреть на источник Windows printf
, но фактическая функция, которая делает плавание-> преобразование строк, _cfltcvt_l
и его источник, кажется, не доступен.)
Править: график утолщает! Теория об этом вызываемом неточным представлением могла бы быть неправильной, потому что 0.125 действительно имеет точное двоичное представление, и это все еще отличается, когда произведено с '%.2f' % 0.125
:
Windows: 0.13
Linux: 0.12
Однако round(0.125, 2)
возвраты 0.13 и в Windows и в Linux.
Короткий ответ, для простоты и простоты использования, вы действительно не можете пойти не так с PostSharp.
Более длинный ответ: По моему мнению, вы должны выбирать между двумя рамками в зависимости от того, чего вы пытаетесь достичь.
Если вы хотите, чтобы аспекты, которые должны изменяться на основе контекста, учитывайте Spring.NET (или любую aop-инфраструктуру, которая вводит код во время выполнения на основе конфигурации). Это позволяет настраивать поведение объектов в зависимости от выполняемых действий. Например, с помощью вашей конфигурации вы можете использовать один тип регистрации в консольном приложении, а другой в веб-приложении. Обратите внимание, что Spring также является контейнером DI (и некоторые другие вещи) - он выходит за рамки AOP, и, безусловно, стоит научиться использовать.
С другой стороны, если вы хотите, чтобы поведение, которое должно всегда действовать, независимо от контекста, то PostSharp (компиляция ткачества времени) является вашим лучшим.
Для того, что вы делаете, я рекомендую начать с PostSharp.
-121--3190185-мне пришлось изменить код для передачи в идентификаторе пользователя и pwd.
Set objIADS = GetObject("WinNT:").OpenDSObject("WinNT://" & strDomain, strUsername, strPassword, ADS_SECURE_AUTHENTICATION)
Set objIADSUser = objIADS.GetObject("user", strUsername)
For each Member in objIADSUser.Groups
If Member.Class = "Group" then
If Member.Name = "TEST_AD_GROUP" then
x = "true"
EXIT FOR
End If
End If
Next
-121--4407412- Во-первых, это звучит так, как будто Windows имеет неправильное право в данном случае (не то, что это действительно имеет значение). Стандарт C требует, чтобы значение, выводимое на % .2f
, округлялось до соответствующего числа цифр . Наиболее известным алгоритмом для этого является dtoa , реализованный Дэвидом М. Геем . Вероятно, это можно перенести в Windows или найти встроенную реализацию.
Если вы еще не прочитали "Как точно печатать числа с плавающей запятой" от Стила и Уайта, найдите копию и прочитайте ее. Это определенно просветительное чтение. Обязательно найдите оригинал с конца 70-х годов. Я думаю, что в какой-то момент я приобрел свое у ACM или IEEE.
Десятичный модуль дает у вас есть доступ к нескольким режимам округления:
import decimal
fs = ['11.544','11.545','11.546']
def convert(f,nd):
# we want 'nd' beyond the dec point
nd = f.find('.') + nd
c1 = decimal.getcontext().copy()
c1.rounding = decimal.ROUND_HALF_UP
c1.prec = nd
d1 = c1.create_decimal(f)
c2 = decimal.getcontext().copy()
c2.rounding = decimal.ROUND_HALF_DOWN
c2.prec = nd
d2 = c2.create_decimal(f)
print d1, d2
for f in fs:
convert(f,2)
Вы можете построить десятичную дробь из целого числа или строки. В вашем случае подайте ему строку с большим количеством цифр, чем вы хотите, и обрежьте, установив context.prec.
Вот ссылка на сообщение pymotw с подробным обзором десятичного модуля:
http://broadcast.oreilly.com/2009/08/pymotw-decimal---fixed-and-flo. html
Возможно, вы сможете вычесть крошечную сумму из значения, чтобы принудительно округлить в меньшую сторону
print "%.2f"%(11.545-1e-12)
Вы можете попробовать вычесть (или сложить для отрицательного числа) небольшую дельту, которая не повлияет на округление для чисел, достаточно далеко отстоящих от точности.
Например, если вы округляете %. 2f
, попробуйте эту версию в Windows:
printf("%.2f", 11.545 - 0.001);
Числа с плавающей запятой, как известно, проблематичны, если вы не знаете, что происходит под крышкой. В этом случае лучше всего написать (или использовать) библиотеку десятичных типов, чтобы облегчить проблемы.
Программа-пример:
#include <stdio.h>
int main (void) {
printf("%.20f\n", 11.545);
printf("%.2f\n", 11.545);
printf("%.2f\n", 11.545 + 0.001);
return 0;
}
выводит это в моей среде Cygwin:
11.54499999999999992895
11.54
11.55
что нормально для вашего конкретного случая (это идет не так, но, надеюсь, применимо и в другом направлении: вам нужно проверить это) но вы должны проверить весь возможный диапазон ввода, если хотите быть уверенным, что это сработает для всех ваших случаев.
Обновление:
Евгений, на основе вашего комментария:
Это работает для этого конкретного случая, но не как общее решение. Например, если число, которое я хочу отформатировать, равно 0,545 вместо 11,545, то «% .2f»% (0,545–0,001) возвращает «0,54», а «% .2f»% 0,545 в Linux правильно возвращает «0,55».
поэтому я сказал, что вам нужно будет проверить весь диапазон, чтобы увидеть, будет ли он работать, и почему я указал, что предпочтительнее использовать десятичный тип данных.
Если вам нужна десятичная точность, это то, что вам нужно сделать. Но вы можете рассмотреть случаи в этом диапазоне, где Linux тоже идет другим путем (согласно вашему комментарию) - может возникнуть ситуация, когда Linux и Windows не согласны в направлении, противоположном тому, что вы нашли - десятичный тип, вероятно, выиграл не решаю это.
Возможно, вам придется сделать ваши инструменты сравнения немного более интеллектуальными, поскольку они могут игнорировать разницу в 1 в последней дробной части.
Думаю, в разных ситуациях лучше другой метод. Например, если необходимо обработать возвращаемое значение перед возвращением, необходимо иметь одну точку выхода. Но в других ситуациях удобнее использовать несколько возвратов.
Одна записка. Если возвращаемое значение должно обрабатываться до возврата в нескольких ситуациях, но не во всех, наилучшие решения (IMHO) для определения метода, такого как ProcessVal, и вызова его перед возвращением:
var retVal = new RetVal();
if(!someCondition)
return ProcessVal(retVal);
if(!anotherCondition)
return retVal;
-121--1768986- Одним из вариантов было бы
>>> var test = "Mar 16, 2010 00:00 AM";
>>> test.replace(test.substring(13,15),"12")
-121--2764416- Я не думаю, что Windows делает что-то особенно умное (например, пытается переосмыслить поплавок в базе 10) здесь: Я бы предположил, что это просто точное вычисление первых 17 значащих цифр (что даст '11.545000000000000'), а затем прикрепление дополнительных нулей в конце, чтобы составить требуемое количество мест после точки.
Как говорят другие, различные результаты для 0.125 получены от Windows, использующей округление наполовину вверх, и Linux, использующей округление наполовину четно.
Обратите внимание, что для Python 3,1 (и Python 2,7, когда он появляется) результат форматирования float будет независимым от платформы (за исключением, возможно, необычных платформ).
Рассмотрите возможность сравнения чисел с плавающей точкой с некоторым допуском/эпсилоном. Это гораздо надежнее, чем пытаться найти точное соответствие.
Я имею в виду, что кроме того, что два числа с плавающей точкой равны, когда:
f1 == f2
Скажите, что они равны, когда:
fabs(f1 - f2) < eps
Для некоторого малого eps
. Более подробную информацию по этому вопросу можно найти здесь