OF标记位判断

转自:http://hi.baidu.com/neomanontheway/blog/item/7ada62db3852a12511df9bfd.html

•计算机硬件判断溢出的方法如下:
最高位有进位但次高位没有进位,或者最高位无进位但次高位有进位;也就是最高位和次高位一个有进位一个无进位。

•我们不是机器不使用这么机械的方法,可以使用如下规则(汇编教材第二版第19页):
只有当两个相同符号数相加(含两个不同符号数相减),而运算结果的符号与原数据符号相反时,产生溢出。

•例如:3AH+7CH=00111010B+01111100B=10110110B=B6H
次高位一个是0一个是1,但还有一个低位进位1,所以次高位有进位;最高位两个0,还有次高位进位,结果只是1没有进位,所以计算机判断有溢出。
我们判断,看最高位,两个数最高位相同为0,相加后结果最高位为1,所以有溢出。

再如:3AH+8DH,我们可以马上判断出没有溢出。因为这是两个不同符号数相加。

利用调试程序可以帮助你验证。

将数据按有符号数进行补码变换求出对应十进制真值,然后判断也可以,但比较麻烦,不如上述规则简单明了。


提问:

进位和溢出的区别,书上也有解释的。但是我看不太明白。我理解的是,进位和无符号数有关,溢出和有符号数有关。书上说,有进位时,运算结果正确,有溢出时,运算结果已经不正确了。这句话不能理解。您能不能简单地帮我说说呢?

回复:

用8位(字节量)数据相加为例说明溢出后运算结果不正确:

对数据3AH和7CH相加,80x86处理器按照无符号数相加,和是B6H;
转换为十进制表达,就是58+124=182,8位无符号数表达0~255范围,
所以没有超出这个范围、没有进位CF=0。

8位补码表达的有符号数的范围是-128~+127,182超出了这个范围,所以产生溢出、OF=1;
另一方面,补码B6H表达真值是-74;两个正数相加、和不可能是负数,显然运算结果不正确。

<think> 我们被要求使用Java注解来实现小数位数的判断。注解本身不包含业务逻辑,但我们可以通过注解来标记需要进行小数位校验的字段,然后在运行时通过反射或AOP等方式进行校验。 思路: 1. 定义一个注解,比如@Decimal,用于标记需要校验小数位数的字段。 2. 注解可以包含两个属性:整数部分的最大位数(可选)和小数部分的最大位数(必须)。 3. 通过编写校验工具类,利用正则表达式或BigDecimal等方法来校验小数位数。 4. 在运行时,我们可以编写一个校验器,遍历对象中被@Decimal注解标记的字段,并调用校验工具进行校验。 步骤: 1. 定义注解: ```java import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) // 只能应用于字段上 @Retention(RetentionPolicy.RUNTIME) // 运行时保留,这样我们可以通过反射获取 public @interface Decimal { int integerMax() default Integer.MAX_VALUE; // 整数部分最大位数,默认不限制 int fractionMax(); // 小数部分最大位数 } ``` 2. 编写校验工具方法(这里使用正则表达式): ```java import java.math.BigDecimal; import java.util.regex.Pattern; public class DecimalValidator { // 使用正则表达式校验 public static boolean validateByRegex(String value, int integerMax, int fractionMax) { if (value == null) { return true; // 如果为null,跳过校验(或者根据需求返回false,这里假设非空校验由其他注解处理) } // 构建正则表达式:整数部分最多integerMax位,小数部分最多fractionMax位 String regex = String.format("^-?\\d{1,%d}(\\.\\d{1,%d})?$", integerMax, fractionMax); return Pattern.matches(regex, value); } // 或者使用BigDecimal方式(更精确,可以处理科学计数法等,但需要将数字转换为字符串时注意格式) public static boolean validateByBigDecimal(BigDecimal value, int integerMax, int fractionMax) { if (value == null) { return true; } // 将BigDecimal转换为字符串,避免科学计数法 String str = value.stripTrailingZeros().toPlainString(); // 拆分整数部分和小数部分 String[] parts = str.split("\\."); String integerPart = parts[0]; // 如果整数部分以负号开头,去掉负号(因为负号不影响位数) if (integerPart.startsWith("-")) { integerPart = integerPart.substring(1); } // 整数部分位数 int integerDigits = integerPart.length(); int fractionDigits = parts.length > 1 ? parts[1].length() : 0; return integerDigits <= integerMax && fractionDigits <= fractionMax; } } ``` 3. 编写一个校验器,用于校验一个对象中所有被@Decimal注解标记的字段: ```java import java.lang.reflect.Field; public class DecimalChecker { public static void validate(Object obj) throws IllegalAccessException, DecimalValidationException { Class<?> clazz = obj.getClass(); for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(Decimal.class)) { Decimal decimalAnnotation = field.getAnnotation(Decimal.class); field.setAccessible(true); Object value = field.get(obj); if (value == null) { continue; // 跳过null } // 处理不同的类型:可能为String、BigDecimal、Double等 if (value instanceof String) { if (!DecimalValidator.validateByRegex((String)value, decimalAnnotation.integerMax(), decimalAnnotation.fractionMax())) { throw new DecimalValidationException(field.getName() + "格式错误,小数位数超过限制"); } } else if (value instanceof BigDecimal) { if (!DecimalValidator.validateByBigDecimal((BigDecimal)value, decimalAnnotation.integerMax(), decimalAnnotation.fractionMax())) { throw new DecimalValidationException(field.getName() + "格式错误,小数位数超过限制"); } } else if (value instanceof Double || value instanceof Float) { // 将浮点数转换为BigDecimal BigDecimal decimalValue = value instanceof Double ? BigDecimal.valueOf((Double)value) : BigDecimal.valueOf((Float)value); if (!DecimalValidator.validateByBigDecimal(decimalValue, decimalAnnotation.integerMax(), decimalAnnotation.fractionMax())) { throw new DecimalValidationException(field.getName() + "格式错误,小数位数超过限制"); } } else { // 其他类型不处理 } } } } } // 自定义异常 public class DecimalValidationException extends Exception { public DecimalValidationException(String message) { super(message); } } ``` 4. 使用注解标记字段: ```java public class User { @Decimal(integerMax = 5, fractionMax = 2) private BigDecimal salary; @Decimal(fractionMax = 3) // 整数部分不限制(默认Integer.MAX_VALUE) private String discount; // 其他字段,getter和setter } ``` 5. 在需要校验的地方调用校验器: ```java User user = new User(); user.setSalary(new BigDecimal("12345.67")); // 整数部分5位,小数部分2位,符合 user.setDiscount("0.1234"); // 小数部分最大3位,这里4位,会抛出异常 try { DecimalChecker.validate(user); } catch (DecimalValidationException e) { e.printStackTrace(); } ``` 注意:使用正则表达式的方法对于整数部分和小数部分分开校验,但要注意负号。另外,使用BigDecimal的方法更精确,但要注意浮点数的精度问题(如使用Double和Float时,建议使用字符串构造BigDecimal,但这里我们使用了BigDecimal.valueOf,它使用Double.toString方法,可以避免一些精度问题,但如果是浮点数直接赋值,可能会有精度损失,所以最好在实体中直接使用BigDecimal类型)。 另外,正则表达式方法只能校验字符串形式,所以如果字段是BigDecimal或浮点数类型,我们需要将其转为字符串,但要注意科学计数法的问题。上面正则表达式方法只适用于字符串字段,而BigDecimal方法适用于数值类型。 因此,在校验器里我们根据字段类型分别处理:字符串用正则,数值类型用BigDecimal方法。 但是,我们也可以统一使用BigDecimal方法:将任何数值类型都转为BigDecimal,然后转为字符串(使用toPlainString)再拆分。这样,我们可以统一处理。同时,字符串类型的数值也可以先转为BigDecimal(但要考虑字符串可能不是数字的情况)。所以,我们可以改进一下: 在DecimalValidator中,我们提供一个统一的方法,将任意对象转换为字符串(对于非字符串,先转为BigDecimal再转为字符串,或者直接调用toString?但这样不准确,比如Double的toString可能包含科学计数法)。因此,我们推荐在实体类中,小数相关的字段使用BigDecimal类型,这样避免精度问题。 修改校验器,统一使用BigDecimal方式(但要求字段必须是BigDecimal类型,或者可以转换为BigDecimal的字符串)?这样会限制字段类型。所以,我们保留两种方式,在注解中增加一个属性来指定使用哪种方式?或者根据字段类型自动选择。 实际上,我们可以在校验器里这样处理: - 如果是BigDecimal、Double、Float,则使用BigDecimal方法(先转为BigDecimal) - 如果是String,则使用正则表达式(因为字符串可能不是一个有效的数字,正则可以提前排除非数字字符串,但这里我们假设字符串已经是数字格式) 但是,为了灵活性,我们也可以将字符串用BigDecimal解析,然后使用BigDecimal方法校验,这样统一。但这样如果字符串不是数字,会抛出异常。所以,我们可以在校验之前先判断字符串是否是数字(用正则判断是否是数字格式),然后再转为BigDecimal。 考虑到复杂度,我们这里不展开,用户可以根据需求调整。 因此,我们上面的示例代码已经提供了两种方式的处理。 注意:对于BigDecimal,我们使用stripTrailingZeros()去掉末尾的0,然后toPlainString()得到普通字符串,这样避免科学计数法,然后拆分整数和小数部分。 但是,如果整数部分是0,例如0.123,拆分后的整数部分可能是"0",所以我们的方法可以正确处理。 总结:我们通过自定义注解,结合反射和校验工具类,实现了小数位数的校验。 以上代码仅供参考,实际应用中可能需要根据具体需求调整。 引用说明: 我们在实现中参考了引用[2]中关于BigDecimal设置小数位的方法,以及引用[3]中关于小数位数校验的思路(正则表达式)。同时,引用[4]提到了BigDecimal和四舍五入的搭配使用,但我们的校验不需要四舍五入,而是直接检查位数。 但是,请注意:我们的校验是在运行时通过反射进行的,可能会对性能有一定影响。如果对性能要求高,可以考虑在编译时使用注解处理器(但通常用于生成代码,不适合做业务校验)或者使用AOP在方法调用前校验。 另外,我们也可以结合JSR 303 Bean Validation规范,自定义一个校验注解,然后通过Hibernate Validator等实现校验。这样更符合标准。 下面补充使用JSR 303的方式: 1. 定义注解: ```java import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE}) @Retention(RUNTIME) @Constraint(validatedBy = DecimalValidatorForJSR.class) // 指定校验器 @Documented public @interface Decimal { String message() default "小数位数超过限制"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; int integerMax() default Integer.MAX_VALUE; int fractionMax(); } ``` 2. 编写校验器(实现ConstraintValidator接口): ```java import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.math.BigDecimal; public class DecimalValidatorForJSR implements ConstraintValidator<Decimal, Object> { private int integerMax; private int fractionMax; @Override public void initialize(Decimal constraintAnnotation) { this.integerMax = constraintAnnotation.integerMax(); this.fractionMax = constraintAnnotation.fractionMax(); } @Override public boolean isValid(Object value, ConstraintValidatorContext context) { if (value == null) { return true; } BigDecimal decimalValue; if (value instanceof String) { try { decimalValue = new BigDecimal((String) value); } catch (NumberFormatException e) { return false; // 转换失败,说明不是有效的数字字符串 } } else if (value instanceof BigDecimal) { decimalValue = (BigDecimal) value; } else if (value instanceof Double) { decimalValue = BigDecimal.valueOf((Double) value); } else if (value instanceof Float) { decimalValue = BigDecimal.valueOf((Float) value); } else { // 不支持的类型 return false; } String str = decimalValue.stripTrailingZeros().toPlainString(); String[] parts = str.split("\\."); String integerPart = parts[0]; if (integerPart.startsWith("-")) { integerPart = integerPart.substring(1); } int integerDigits = integerPart.length(); int fractionDigits = parts.length > 1 ? parts[1].length() : 0; return integerDigits <= integerMax && fractionDigits <= fractionMax; } } ``` 3. 在实体类上使用注解: ```java public class User { @Decimal(integerMax = 5, fractionMax = 2, message = "薪水整数部分不超过5位,小数部分不超过2位") private BigDecimal salary; // ... 其他 } ``` 4. 在需要校验的地方使用标准的校验方式(如Spring的@Valid注解)。 这种方式更符合企业级应用的标准。 因此,如果项目已经使用了JSR 303,推荐使用第二种方式。 根据用户的需求,他要求使用注解,但没有说明是否使用JSR 303,所以我们提供了两种方案。 相关问题: 1. 如何在Java中自定义注解? 2. 如何通过反射获取注解并实现校验逻辑? 3. JSR 303 Bean Validation是什么?如何自定义校验注解? 4. BigDecimal在Java中如何处理小数位数? 5. 使用正则表达式校验数字格式的注意事项有哪些? 希望这些能帮助到你! </think>### Java注解实现小数位判断的方法 在Java中,可以通过自定义注解结合反射机制实现小数位数的校验。以下是完整的实现方案: #### 1. 定义小数位校验注解 ```java import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface DecimalScale { int value() default 2; // 默认允许2位小数 String message() default "小数位数不能超过{value}位"; } ``` #### 2. 实现校验逻辑工具类 ```java import java.lang.reflect.Field; import java.math.BigDecimal; public class DecimalValidator { public static void validate(Object obj) throws IllegalAccessException { Class<?> clazz = obj.getClass(); for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(DecimalScale.class)) { field.setAccessible(true); DecimalScale annotation = field.getAnnotation(DecimalScale.class); int maxScale = annotation.value(); Object value = field.get(obj); if (value == null) continue; if (value instanceof BigDecimal) { checkBigDecimal((BigDecimal) value, maxScale, field.getName()); } else if (value instanceof Double || value instanceof Float) { checkDecimal(value.toString(), maxScale, field.getName()); } else { throw new IllegalArgumentException( field.getName() + "字段类型必须是BigDecimal/Double/Float" ); } } } } private static void checkBigDecimal(BigDecimal value, int maxScale, String fieldName) { if (value.scale() > maxScale) { throw new IllegalArgumentException( fieldName + "小数位数超过限制(最大允许" + maxScale + "位)" ); } } private static void checkDecimal(String valueStr, int maxScale, String fieldName) { int dotIndex = valueStr.indexOf('.'); if (dotIndex != -1 && (valueStr.length() - dotIndex - 1) > maxScale) { throw new IllegalArgumentException( fieldName + "小数位数超过限制(最大允许" + maxScale + "位)" ); } } } ``` #### 3. 在实体类中使用注解 ```java public class Product { @DecimalScale(value = 2, message = "价格小数位不能超过2位") private BigDecimal price; @DecimalScale(3) // 允许最多3位小数 private Double weight; // 构造方法和getter/setter } ``` #### 4. 使用示例 ```java public class Main { public static void main(String[] args) { Product product = new Product(); product.setPrice(new BigDecimal("123.456")); // 3位小数 product.setWeight(5.6789); // 4位小数 try { DecimalValidator.validate(product); } catch (Exception e) { System.err.println("校验失败: " + e.getMessage()); // 输出: 校验失败: price小数位数超过限制(最大允许2位) } } } ``` #### 实现原理说明 1. **注解定义**:`@DecimalScale` 标注在字段上,指定允许的最大小数位数 2. **反射处理**:通过反射获取被注解标记的字段值 3. **类型处理**: - 对 `BigDecimal` 类型:直接使用 `scale()` 方法获取小数位数 - 对 `Double/Float` 类型:转换为字符串后检查小数点后的位数 4. **校验触发**:调用 `DecimalValidator.validate()` 时自动执行校验 #### 方案特点 - **精确校验**:直接处理原始数据类型,避免字符串转换精度问题 - **灵活配置**:通过注解参数自定义小数位数限制 - **类型安全**:强制校验数值类型字段 - **易扩展**:可结合 Spring AOP 实现自动校验[^2][^4] --- ### 相关问题 1. 如何处理Java浮点数的精度丢失问题? 2. 如何将小数位校验注解集成到Spring Validation框架? 3. BigDecimal的setScale()方法有哪些舍入模式? 4. 除了注解方式,还有哪些方法可以限制小数位数? 5. 如何实现自定义注解的参数动态配置? [^1]: Java中小数位数的限制可以通过多种方式实现,注解方式提供声明式校验能力 [^2]: BigDecimal与setScale方法的结合是处理小数精度的标准做法 [^3]: 正则表达式也可用于字符串格式的小数位数校验 [^4]: 四舍五入处理推荐使用RoundingMode.HALF_UP银行家舍入法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值