我与编程一些旧软件PLC(500的RSLogix,不问),它本身不支持模运算,但我需要一个。 我没有访问:模数,整数除法,局部变量,一个截断操作(虽然我可以用四舍五入破解它)。 此外,提供给我的所有变量都通过数据类型排序表布局。 最后,它应该工作浮点小数,例如12345.678 MOD 10000 = 2345.678
。
如果我们让我们的公式:
dividend / divisor = integer quotient, remainder
有两个明显的实现。
实施1:执行浮点除法: dividend / divisor = decimal quotient
。 然后,等你发现了一起黑客截断操作integer quotient
。 由乘以divisor
并找到之间的区别dividend
和,这导致remainder
。
因为它涉及了一堆不同类型的变量,我不喜欢这样。 我不能“通”变量的子程序,所以我只需要分配一些位于多个不同的变量表全局变量,它是难以遵循。 不幸的是,“难以理解”罪名,因为它需要足够简单的维修工人乱用。
实施2:创建一个循环,使得在dividend > divisor
divisor = dividend - divisor
。 这是非常干净的,但它违反了PLC编程,这是从来没有使用循环,因为如果有人无意中修改索引计数器,你可以得到被困在一个无限循环和机器会发疯或无可挽回故障的大规则之一。 加上循环是很难进行维修排除故障。 另外,我甚至不具有循环的指令,我一定要使用标签和跳跃。 好恶。
所以我想知道如果任何人有任何聪明的数学黑客或模量比其中任一的聪明实现。 我有机会获得+ - * /,指数,平方根,三角函数,对数,ABS值和AND / OR / NOT / XOR。
Answer 1:
如果你不介意的话过于复杂的事情,浪费电脑的时候,你可以计算模量与周期三角函数:
atan(tan(( 12345.678 -5000)*pi/10000))*10000/pi+5000 = 2345.678
虽然严重,减去10000一次或两次(你的“实施2”)是更好的。 对于一般的浮点模量通常算法需要大量的位级操作是不可行或许对你。 例如见http://www.netlib.org/fdlibm/e_fmod.c (算法很简单,但代码是因为特殊情况复杂的,因为它是为IEEE写754张精度数假设没有64位整数型)
Answer 2:
有多少位你处理? 你可以这样做:
if dividend > 32 * divisor dividend -= 32 * divisor
if dividend > 16 * divisor dividend -= 16 * divisor
if dividend > 8 * divisor dividend -= 8 * divisor
if dividend > 4 * divisor dividend -= 4 * divisor
if dividend > 2 * divisor dividend -= 2 * divisor
if dividend > 1 * divisor dividend -= 1 * divisor
quotient = dividend
因为有在位刚刚展开多次dividend
。 请务必要小心那些乘四溢。 这就像你的#2不同的是它的log(n),而不是n次迭代,所以它是可行的完全展开。
Answer 3:
这一切似乎完全过于复杂。 您有在10000上滑过对象一起,其位置你在任何给定的点所跟踪的线滚动编码器的索引。 如果您需要转发的项目停止点或行动要点沿线,只需添加你需要然而,许多英寸,并立即减去10000,如果你的目标结果大于10000。
或者,或另外,你总是得到一个新的编码器值每PLC扫描。 在当前值和最终值之间的差为负的情况下,你可以激发工作接触标志卷绕事件并作出适当的修正为上扫描任何计算。 (**或递增如下二次计数器)
不知道更多有关的实际问题,这是很难提出一个更具体的解决方案,但肯定有更好的解决方案。 我不认为有必要在此MOD在所有。 此外,在地板上的家伙会感谢你不是模糊的向导的东西填满机。
我引用 :
最后,它具有浮点小数,例如12345.678 MOD 10000 = 2345.678工作
有存在做这个辉煌的功能-它是一个减法 。 为什么它需要比这更复杂? 如果您的输送线比833英尺实际上不再是再滚上一个主索引翻车递增,直到你有足够的距离来覆盖你需要接地的第二计数器。
例如,如果需要传送机存储器的十万英寸你可以有在10处主编码器翻车翻转二次计数器可以如上所述很容易地检测和你每次递增次级计数器。 你的工作编码器的位置,然后是10000次计数器值加上当前编码器值。 仅在延长的单位工作,并辅助柜台任何价值,你需要不丢失任何部件翻身。 该问题,再次,然后减小到简单的减法(如上述)。
我使用这种技术用行星齿轮转动部件保持件,例如。 我有滑过每个主旋转一次,而行星齿轮卫星部件(其本身周围的定子齿轮旋转)需要43个主旋转以返回到相同的起始取向的编码器。 与主编码器翻转点的简单计数器递增(或递减,这取决于方向)它给你的部分是在其中一个完全绝对度量。 在这种情况下,次级计数器在43翻转。
这同样工作与为一个线性输送机可以持续的距离无限远处的唯一区别线性输送。 那么问题只需要通过在该行上的最坏情况的部分截取的最长线性路径的限制。
随着我从来没有使用的RSLogix告诫,这里的总体思路是(我这里用通用的符号和我的语法可能是有点不对,但你应该明白我的意思)
通过上述,您结束了一个价值ENC_EXT
已基本转化编码器从10K英寸一至100K寸一体。 我不知道,如果你的输送机反向运行,如果可以,您将需要处理也降计数。 如果你的程序的整个剩下的只有与工作ENC_EXT
值,那么你甚至不必担心的事实,编码器只到10K。 现在进入到100K(或任何你想要的)和环绕可以用减法而不是模处理。
后记:
PLC是首要的状态机。 对于PLC程序的最佳解决方案通常是那些在和谐与这种想法。 如果你的硬件不足以完全代表机器的状态,则PLC程序应该尽力填补与它具有信息丢失的状态信息的空白。 上述解决方案做到这一点 - 它需要的状态信息的不足万英寸并将其扩展,以满足工艺要求。
这种方法的好处是,你现在有保留绝对的状态信息,不只是为输送机,同时也为线路上的任何部分。 您可以向前和向后跟踪他们进行故障排除和调试,你有一个更简单,更清晰的坐标系与合作为将来的扩展。 随着模数计算,你扔掉的状态信息,并试图解决一个功能性的方式个别问题 - 这往往是不与PLC工作的最佳途径。 你种得忘记你从其他编程语言和工作经历,以不同的方式是什么。 PLC是一个不同的野兽,当这样对待他们最好的工作。
Answer 4:
您可以使用一个子程序做你正在谈论什么。 你可以掖棘手的代码,以便离开维修科技股永远不会遇到它。 这几乎肯定是最简单的为您和您的维修人员来了解。
它已经有一段时间,因为我用RSLogix500,所以我可能会得到几个方面的错误,但你会得到点。
定义一个数据文件中每个你浮点和整数,并给他们沿着MOD_F和MOD_N的线符号的东西。 如果你把这些吓人的是,科技股的维护让他们独自一人,和你需要他们为你的数学过程中传递参数和工作区。
如果你真的很担心他们搞乱了数据表中,也有保护自己的方法,但我已经忘记了他们是在SLC / 500什么。
接下来,定义一个子程序,远从数字中使用的那些现在,如果可能的话。 将它命名为类似模量。 同样,维护球员几乎总是留出的SBR,如果他们听起来像编程名称。
在您JSR指令前,梯级,加载要处理到MOD_N和MOD_F数据文件的变量。 与他们加载模量SBR数据的指令评论这些梯级。 使意见明确与编程背景的人。
打电话给你的JSR有条件的,只有当你需要。 维护科技股也懒得故障排除非执行逻辑,所以如果你的JSR不活跃,他们很少会看它。
现在,你有你自己的小围墙的花园,你可以写你的循环无需维护卷入它。 只有使用这些数据文件,并且不承担任何事物的状态,但这些文件是你所期望的。 换句话说,你不能相信间接寻址。 基址寻址是OK的,只要你定义模量JSR中的索引。 不要相信任何进入指数。 这是很容易与您的MOD_N文件中的一个字,一个跳跃和标签写一个for循环。 你的整个执行#2应少于10个梯级左右。 我会考虑使用一个表达式指令或东西...就是那个让你只需要输入一个表达式。 可能需要为该指令504或505。 可以很好地用于组合浮动/整数运算。 检查结果虽然以确保四舍五入不会杀了你。
设置完毕后,验证码,完全如果可能的话。 如果此代码以往任何时候都将导致一个数学溢出和故障的处理器,你永远不会听到它的结束。 它运行在模拟器上,如果你有一个,用怪异的值(如果它们在某种程度上陷入困境的功能输入的负载),并确保PLC不会指手画脚。
如果你做的一切,没有人会永远甚至意识到你在PLC中使用常规的编程技术,你将被罚款。 只要它的作品。
Answer 5:
这是基于由@Keith兰德尔回答一个循环,但还保持由减法除法的结果。 我不停的的printf对清晰度。
#include <stdio.h>
#include <limits.h>
#define NBIT (CHAR_BIT * sizeof (unsigned int))
unsigned modulo(unsigned dividend, unsigned divisor)
{
unsigned quotient, bit;
printf("%u / %u:", dividend, divisor);
for (bit = NBIT, quotient=0; bit-- && dividend >= divisor; ) {
if (dividend < (1ul << bit) * divisor) continue;
dividend -= (1ul << bit) * divisor;
quotient += (1ul << bit);
}
printf("%u, %u\n", quotient, dividend);
return dividend; // the remainder *is* the modulo
}
int main(void)
{
modulo( 13,5);
modulo( 33,11);
return 0;
}
文章来源: A clever homebrew modulus implementation